mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
feat: remove loading from model and use DvaModel instead of redundant types such as kAModelType (#47)
* feat: use DvaModel instead of redundant types such as kAModelType * feat: set the type for registerServer * feat: remove loading from model
This commit is contained in:
@ -1,102 +1,116 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { connect, Dispatch } from 'umi';
|
||||
import i18n from 'i18next';
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import { Input, Modal, Form } from 'antd'
|
||||
import styles from './index.less';
|
||||
import type { chunkModelState } from './model'
|
||||
import EditTag from './editTag'
|
||||
import { Form, Input, Modal } from 'antd';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'umi';
|
||||
import EditTag from './editTag';
|
||||
|
||||
type FieldType = {
|
||||
content_ltks?: string;
|
||||
content_ltks?: string;
|
||||
};
|
||||
interface kFProps {
|
||||
dispatch: Dispatch;
|
||||
chunkModel: chunkModelState;
|
||||
getChunkList: () => void;
|
||||
isShowCreateModal: boolean;
|
||||
doc_id: string;
|
||||
chunk_id: string
|
||||
getChunkList: () => void;
|
||||
isShowCreateModal: boolean;
|
||||
doc_id: string;
|
||||
chunk_id: string;
|
||||
}
|
||||
const Index: React.FC<kFProps> = ({ dispatch, getChunkList, doc_id, isShowCreateModal, chunk_id }) => {
|
||||
// const { , chunkInfo } = chunkModel
|
||||
const [important_kwd, setImportantKwd] = useState(['Unremovable', 'Tag 2', 'Tag 3']);
|
||||
const { t } = useTranslation()
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
isShowCreateModal: false
|
||||
}
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
console.log(chunk_id, isShowCreateModal)
|
||||
if (chunk_id && isShowCreateModal) {
|
||||
dispatch({
|
||||
type: 'chunkModel/get_chunk',
|
||||
payload: {
|
||||
chunk_id
|
||||
},
|
||||
callback(info: any) {
|
||||
console.log(info)
|
||||
const { content_ltks, important_kwd = [] } = info
|
||||
form.setFieldsValue({ content_ltks })
|
||||
setImportantKwd(important_kwd)
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [chunk_id, isShowCreateModal])
|
||||
const [form] = Form.useForm()
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
dispatch({
|
||||
type: 'chunkModel/create_hunk',
|
||||
payload: {
|
||||
content_ltks: values.content_ltks,
|
||||
doc_id,
|
||||
chunk_id,
|
||||
important_kwd
|
||||
},
|
||||
callback: () => {
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
isShowCreateModal: false
|
||||
}
|
||||
});
|
||||
getChunkList && getChunkList()
|
||||
}
|
||||
});
|
||||
|
||||
} catch (errorInfo) {
|
||||
console.log('Failed:', errorInfo);
|
||||
}
|
||||
};
|
||||
const Index: React.FC<kFProps> = ({
|
||||
getChunkList,
|
||||
doc_id,
|
||||
isShowCreateModal,
|
||||
chunk_id,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
return (
|
||||
<Modal title="Basic Modal" open={isShowCreateModal} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Form
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
labelCol={{ span: 5 }}
|
||||
wrapperCol={{ span: 19 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="chunk 内容"
|
||||
name="content_ltks"
|
||||
rules={[{ required: true, message: 'Please input value!' }]}
|
||||
>
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<EditTag tags={important_kwd} setTags={setImportantKwd} />
|
||||
</Form>
|
||||
</Modal >
|
||||
// const { , chunkInfo } = chunkModel
|
||||
const [important_kwd, setImportantKwd] = useState([
|
||||
'Unremovable',
|
||||
'Tag 2',
|
||||
'Tag 3',
|
||||
]);
|
||||
const { t } = useTranslation();
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
isShowCreateModal: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getChunk = useCallback(async () => {
|
||||
if (chunk_id && isShowCreateModal) {
|
||||
const data = await dispatch<any>({
|
||||
type: 'chunkModel/get_chunk',
|
||||
payload: {
|
||||
chunk_id,
|
||||
},
|
||||
});
|
||||
|
||||
);
|
||||
}
|
||||
export default connect(({ chunkModel, loading }) => ({ chunkModel, loading }))(Index);
|
||||
if (data?.retcode === 0) {
|
||||
const { content_ltks, important_kwd = [] } = data.data;
|
||||
form.setFieldsValue({ content_ltks });
|
||||
setImportantKwd(important_kwd);
|
||||
}
|
||||
}
|
||||
}, [chunk_id, isShowCreateModal]);
|
||||
|
||||
useEffect(() => {
|
||||
getChunk();
|
||||
}, [getChunk]);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
dispatch({
|
||||
type: 'chunkModel/create_hunk',
|
||||
payload: {
|
||||
content_ltks: values.content_ltks,
|
||||
doc_id,
|
||||
chunk_id,
|
||||
important_kwd,
|
||||
},
|
||||
// callback: () => {
|
||||
// dispatch({
|
||||
// type: 'chunkModel/updateState',
|
||||
// payload: {
|
||||
// isShowCreateModal: false,
|
||||
// },
|
||||
// });
|
||||
// getChunkList && getChunkList();
|
||||
// },
|
||||
});
|
||||
} catch (errorInfo) {
|
||||
console.log('Failed:', errorInfo);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Basic Modal"
|
||||
open={isShowCreateModal}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
labelCol={{ span: 5 }}
|
||||
wrapperCol={{ span: 19 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="chunk 内容"
|
||||
name="content_ltks"
|
||||
rules={[{ required: true, message: 'Please input value!' }]}
|
||||
>
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<EditTag tags={important_kwd} setTags={setImportantKwd} />
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
export default Index;
|
||||
|
||||
@ -1,142 +1,141 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import type { InputRef } from 'antd';
|
||||
import { Input, Space, Tag, theme, Tooltip } from 'antd';
|
||||
interface editTagsProps {
|
||||
tags: any[],
|
||||
setTags: (tags: any[]) => void
|
||||
import { Input, Space, Tag, Tooltip, theme } from 'antd';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
interface EditTagsProps {
|
||||
tags: any[];
|
||||
setTags: (tags: any[]) => void;
|
||||
}
|
||||
const App: React.FC<editTagsProps> = ({ tags, setTags }) => {
|
||||
const { token } = theme.useToken();
|
||||
const EditTag: React.FC<EditTagsProps> = ({ tags, setTags }) => {
|
||||
const { token } = theme.useToken();
|
||||
|
||||
const [inputVisible, setInputVisible] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [editInputIndex, setEditInputIndex] = useState(-1);
|
||||
const [editInputValue, setEditInputValue] = useState('');
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const editInputRef = useRef<InputRef>(null);
|
||||
const [inputVisible, setInputVisible] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [editInputIndex, setEditInputIndex] = useState(-1);
|
||||
const [editInputValue, setEditInputValue] = useState('');
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const editInputRef = useRef<InputRef>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputVisible) {
|
||||
inputRef.current?.focus();
|
||||
useEffect(() => {
|
||||
if (inputVisible) {
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
}, [inputVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
editInputRef.current?.focus();
|
||||
}, [editInputValue]);
|
||||
|
||||
const handleClose = (removedTag: string) => {
|
||||
const newTags = tags.filter((tag) => tag !== removedTag);
|
||||
console.log(newTags);
|
||||
setTags(newTags);
|
||||
};
|
||||
|
||||
const showInput = () => {
|
||||
setInputVisible(true);
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleInputConfirm = () => {
|
||||
if (inputValue && !tags.includes(inputValue)) {
|
||||
setTags([...tags, inputValue]);
|
||||
}
|
||||
setInputVisible(false);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEditInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleEditInputConfirm = () => {
|
||||
const newTags = [...tags];
|
||||
newTags[editInputIndex] = editInputValue;
|
||||
setTags(newTags);
|
||||
setEditInputIndex(-1);
|
||||
setEditInputValue('');
|
||||
};
|
||||
|
||||
const tagInputStyle: React.CSSProperties = {
|
||||
width: 64,
|
||||
height: 22,
|
||||
marginInlineEnd: 8,
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const tagPlusStyle: React.CSSProperties = {
|
||||
height: 22,
|
||||
background: token.colorBgContainer,
|
||||
borderStyle: 'dashed',
|
||||
};
|
||||
|
||||
return (
|
||||
<Space size={[0, 8]} wrap>
|
||||
{tags.map((tag, index) => {
|
||||
if (editInputIndex === index) {
|
||||
return (
|
||||
<Input
|
||||
ref={editInputRef}
|
||||
key={tag}
|
||||
size="small"
|
||||
style={tagInputStyle}
|
||||
value={editInputValue}
|
||||
onChange={handleEditInputChange}
|
||||
onBlur={handleEditInputConfirm}
|
||||
onPressEnter={handleEditInputConfirm}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [inputVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
editInputRef.current?.focus();
|
||||
}, [editInputValue]);
|
||||
|
||||
const handleClose = (removedTag: string) => {
|
||||
const newTags = tags.filter((tag) => tag !== removedTag);
|
||||
console.log(newTags);
|
||||
setTags(newTags);
|
||||
};
|
||||
|
||||
const showInput = () => {
|
||||
setInputVisible(true);
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleInputConfirm = () => {
|
||||
if (inputValue && !tags.includes(inputValue)) {
|
||||
setTags([...tags, inputValue]);
|
||||
}
|
||||
setInputVisible(false);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEditInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleEditInputConfirm = () => {
|
||||
const newTags = [...tags];
|
||||
newTags[editInputIndex] = editInputValue;
|
||||
setTags(newTags);
|
||||
setEditInputIndex(-1);
|
||||
setEditInputValue('');
|
||||
};
|
||||
|
||||
const tagInputStyle: React.CSSProperties = {
|
||||
width: 64,
|
||||
height: 22,
|
||||
marginInlineEnd: 8,
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const tagPlusStyle: React.CSSProperties = {
|
||||
height: 22,
|
||||
background: token.colorBgContainer,
|
||||
borderStyle: 'dashed',
|
||||
};
|
||||
|
||||
return (
|
||||
<Space size={[0, 8]} wrap>
|
||||
{tags.map((tag, index) => {
|
||||
if (editInputIndex === index) {
|
||||
return (
|
||||
<Input
|
||||
ref={editInputRef}
|
||||
key={tag}
|
||||
size="small"
|
||||
style={tagInputStyle}
|
||||
value={editInputValue}
|
||||
onChange={handleEditInputChange}
|
||||
onBlur={handleEditInputConfirm}
|
||||
onPressEnter={handleEditInputConfirm}
|
||||
/>
|
||||
);
|
||||
const isLongTag = tag.length > 20;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
key={tag}
|
||||
closable={index !== 0}
|
||||
style={{ userSelect: 'none' }}
|
||||
onClose={() => handleClose(tag)}
|
||||
>
|
||||
<span
|
||||
onDoubleClick={(e) => {
|
||||
if (index !== 0) {
|
||||
setEditInputIndex(index);
|
||||
setEditInputValue(tag);
|
||||
e.preventDefault();
|
||||
}
|
||||
const isLongTag = tag.length > 20;
|
||||
const tagElem = (
|
||||
<Tag
|
||||
key={tag}
|
||||
closable={index !== 0}
|
||||
style={{ userSelect: 'none' }}
|
||||
onClose={() => handleClose(tag)}
|
||||
>
|
||||
<span
|
||||
onDoubleClick={(e) => {
|
||||
if (index !== 0) {
|
||||
setEditInputIndex(index);
|
||||
setEditInputValue(tag);
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
|
||||
</span>
|
||||
</Tag>
|
||||
);
|
||||
return isLongTag ? (
|
||||
<Tooltip title={tag} key={tag}>
|
||||
{tagElem}
|
||||
</Tooltip>
|
||||
) : (
|
||||
tagElem
|
||||
);
|
||||
})}
|
||||
{inputVisible ? (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
style={tagInputStyle}
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleInputConfirm}
|
||||
onPressEnter={handleInputConfirm}
|
||||
/>
|
||||
) : (
|
||||
<Tag style={tagPlusStyle} onClick={showInput}>
|
||||
添加关键词
|
||||
</Tag>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
|
||||
</span>
|
||||
</Tag>
|
||||
);
|
||||
return isLongTag ? (
|
||||
<Tooltip title={tag} key={tag}>
|
||||
{tagElem}
|
||||
</Tooltip>
|
||||
) : (
|
||||
tagElem
|
||||
);
|
||||
})}
|
||||
{inputVisible ? (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
style={tagInputStyle}
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleInputConfirm}
|
||||
onPressEnter={handleInputConfirm}
|
||||
/>
|
||||
) : (
|
||||
<Tag style={tagPlusStyle} onClick={showInput}>
|
||||
添加关键词
|
||||
</Tag>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default EditTag;
|
||||
|
||||
@ -1,225 +1,282 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { useNavigate, connect, Dispatch } from 'umi'
|
||||
import { Card, Row, Col, Input, Select, Switch, Pagination, Spin, Button, Popconfirm } from 'antd';
|
||||
import { MinusSquareOutlined, DeleteOutlined, } from '@ant-design/icons';
|
||||
import { api_host } from '@/utils/api';
|
||||
import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil';
|
||||
import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons';
|
||||
import type { PaginationProps } from 'antd';
|
||||
import { api_host } from '@/utils/api'
|
||||
import CreateModal from './components/createModal'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Input,
|
||||
Pagination,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Select,
|
||||
Spin,
|
||||
Switch,
|
||||
} from 'antd';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch, useNavigate, useSelector } from 'umi';
|
||||
import CreateModal from './components/createModal';
|
||||
|
||||
|
||||
import styles from './index.less'
|
||||
import { debounce } from 'lodash';
|
||||
import type { chunkModelState } from './model'
|
||||
interface chunkProps {
|
||||
dispatch: Dispatch;
|
||||
chunkModel: chunkModelState;
|
||||
doc_id: string
|
||||
import styles from './index.less';
|
||||
|
||||
interface PayloadType {
|
||||
doc_id: string;
|
||||
keywords?: string;
|
||||
available_int?: number;
|
||||
}
|
||||
const Index: React.FC<chunkProps> = ({ chunkModel, dispatch, doc_id }) => {
|
||||
const [keywords, SetKeywords] = useState('')
|
||||
const [available_int, setAvailableInt] = useState(-1)
|
||||
const navigate = useNavigate()
|
||||
const [pagination, setPagination] = useState({ page: 1, size: 30 })
|
||||
|
||||
interface IProps {
|
||||
doc_id: string;
|
||||
}
|
||||
|
||||
const Chunk = ({ doc_id }: IProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const chunkModel = useSelector((state: any) => state.chunkModel);
|
||||
const [keywords, SetKeywords] = useState('');
|
||||
const [available_int, setAvailableInt] = useState(-1);
|
||||
const navigate = useNavigate();
|
||||
const [pagination, setPagination] = useState({ page: 1, size: 30 });
|
||||
// const [datas, setDatas] = useState(data)
|
||||
const { data = [], total, loading, chunk_id, isShowCreateModal } = chunkModel
|
||||
console.log(chunkModel)
|
||||
const { data = [], total, chunk_id, isShowCreateModal } = chunkModel;
|
||||
const effects = useSelector((state: any) => state.loading.effects);
|
||||
const loading = getOneNamespaceEffectsLoading('chunkModel', effects, [
|
||||
'create_hunk',
|
||||
'chunk_list',
|
||||
'switch_chunk',
|
||||
]);
|
||||
|
||||
const getChunkList = (value?: string) => {
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
interface payloadType {
|
||||
doc_id: string;
|
||||
keywords?: string;
|
||||
available_int?: number
|
||||
}
|
||||
const payload: payloadType = {
|
||||
const payload: PayloadType = {
|
||||
doc_id,
|
||||
keywords: value || keywords,
|
||||
available_int
|
||||
}
|
||||
available_int,
|
||||
};
|
||||
if (payload.available_int === -1) {
|
||||
delete payload.available_int
|
||||
delete payload.available_int;
|
||||
}
|
||||
dispatch({
|
||||
type: 'chunkModel/chunk_list',
|
||||
payload: {
|
||||
...payload,
|
||||
...pagination
|
||||
}
|
||||
});
|
||||
}
|
||||
const confirm = (id: string) => {
|
||||
console.log(id)
|
||||
dispatch({
|
||||
type: 'chunkModel/rm_chunk',
|
||||
payload: {
|
||||
chunk_ids: [id]
|
||||
...pagination,
|
||||
},
|
||||
callback: getChunkList
|
||||
});
|
||||
};
|
||||
const confirm = async (id: string) => {
|
||||
const retcode = await dispatch<any>({
|
||||
type: 'chunkModel/rm_chunk',
|
||||
payload: {
|
||||
chunk_ids: [id],
|
||||
},
|
||||
});
|
||||
|
||||
retcode === 0 && getChunkList();
|
||||
};
|
||||
|
||||
const handleEditchunk = (chunk_id?: string) => {
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
isShowCreateModal: true,
|
||||
chunk_id,
|
||||
doc_id
|
||||
doc_id,
|
||||
},
|
||||
callback: getChunkList
|
||||
});
|
||||
}
|
||||
const onShowSizeChange: PaginationProps['onShowSizeChange'] = (page, size) => {
|
||||
setPagination({ page, size })
|
||||
getChunkList();
|
||||
};
|
||||
const switchChunk = (id: string, available_int: boolean) => {
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
dispatch({
|
||||
|
||||
const onShowSizeChange: PaginationProps['onShowSizeChange'] = (
|
||||
page,
|
||||
size,
|
||||
) => {
|
||||
setPagination({ page, size });
|
||||
};
|
||||
|
||||
const switchChunk = async (id: string, available_int: boolean) => {
|
||||
const retcode = await dispatch<any>({
|
||||
type: 'chunkModel/switch_chunk',
|
||||
payload: {
|
||||
chunk_ids: [id],
|
||||
available_int: Number(available_int),
|
||||
doc_id
|
||||
doc_id,
|
||||
},
|
||||
callback: getChunkList
|
||||
});
|
||||
}
|
||||
|
||||
retcode === 0 && getChunkList();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getChunkList()
|
||||
}, [doc_id, available_int, pagination])
|
||||
const debounceChange = debounce(getChunkList, 300)
|
||||
const debounceCallback = useCallback((value: string) => debounceChange(value), [])
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const value = e.target.value
|
||||
SetKeywords(value)
|
||||
debounceCallback(value)
|
||||
}
|
||||
getChunkList();
|
||||
}, [doc_id, available_int, pagination]);
|
||||
|
||||
const debounceChange = debounce(getChunkList, 300);
|
||||
const debounceCallback = useCallback(
|
||||
(value: string) => debounceChange(value),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const value = e.target.value;
|
||||
SetKeywords(value);
|
||||
debounceCallback(value);
|
||||
};
|
||||
const handleSelectChange = (value: number) => {
|
||||
setAvailableInt(value)
|
||||
}
|
||||
console.log('loading', loading)
|
||||
return (<>
|
||||
<div className={styles.chunkPage}>
|
||||
<div className={styles.filter}>
|
||||
<div>
|
||||
<Input placeholder="搜索" style={{ width: 220 }} value={keywords} allowClear onChange={handleInputChange} />
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="是否启用"
|
||||
optionFilterProp="children"
|
||||
value={available_int}
|
||||
onChange={handleSelectChange}
|
||||
style={{ width: 220 }}
|
||||
options={[
|
||||
{
|
||||
value: -1,
|
||||
label: '全部',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '启用',
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: '未启用',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
setAvailableInt(value);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className={styles.chunkPage}>
|
||||
<div className={styles.filter}>
|
||||
<div>
|
||||
<Input
|
||||
placeholder="搜索"
|
||||
style={{ width: 220 }}
|
||||
value={keywords}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="是否启用"
|
||||
optionFilterProp="children"
|
||||
value={available_int}
|
||||
onChange={handleSelectChange}
|
||||
style={{ width: 220 }}
|
||||
options={[
|
||||
{
|
||||
value: -1,
|
||||
label: '全部',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '启用',
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: '未启用',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleEditchunk();
|
||||
}}
|
||||
type="link"
|
||||
>
|
||||
添加分段
|
||||
</Button>
|
||||
</div>
|
||||
<Button onClick={() => { handleEditchunk() }} type='link'>添加分段</Button>
|
||||
</div>
|
||||
<div className={styles.pageContent}>
|
||||
<Spin spinning={loading} className={styles.spin} size='large'>
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24 }} >
|
||||
{
|
||||
data.map((item: any) => {
|
||||
return (<Col className="gutter-row" key={item.chunk_id} xs={24} sm={12} md={12} lg={8}>
|
||||
<Card className={styles.card}
|
||||
onClick={() => { handleEditchunk(item.chunk_id) }}
|
||||
<div className={styles.pageContent}>
|
||||
<Spin spinning={loading} className={styles.spin} size="large">
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24 }}>
|
||||
{data.map((item: any) => {
|
||||
return (
|
||||
<Col
|
||||
className="gutter-row"
|
||||
key={item.chunk_id}
|
||||
xs={24}
|
||||
sm={12}
|
||||
md={12}
|
||||
lg={8}
|
||||
>
|
||||
<img style={{ width: '50px' }} src={`${api_host}/document/image/${item.img_id}`} alt="" />
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.context}>
|
||||
{item.content_ltks}
|
||||
</span>
|
||||
<span className={styles.delete}>
|
||||
<Switch size="small" defaultValue={item.available_int == '1'} onChange={(checked: boolean, e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation(); switchChunk(item.chunk_id, checked)
|
||||
}} />
|
||||
</span>
|
||||
<Card
|
||||
className={styles.card}
|
||||
onClick={() => {
|
||||
handleEditchunk(item.chunk_id);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{ width: '50px' }}
|
||||
src={`${api_host}/document/image/${item.img_id}`}
|
||||
alt=""
|
||||
/>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.context}>
|
||||
{item.content_ltks}
|
||||
</span>
|
||||
<span className={styles.delete}>
|
||||
<Switch
|
||||
size="small"
|
||||
defaultValue={item.available_int == '1'}
|
||||
onChange={(checked: boolean, e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
switchChunk(item.chunk_id, checked);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.doc_num}文档
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.chunk_num}个
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.token_num}千字符
|
||||
</span>
|
||||
<span style={{ float: 'right' }}>
|
||||
<Popconfirm
|
||||
title="Delete the task"
|
||||
description="Are you sure to delete this task?"
|
||||
onConfirm={(e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
console.log(confirm);
|
||||
confirm(item.chunk_id);
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
}}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />{item.doc_num}文档
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />{item.chunk_num}个
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />{item.token_num}千字符
|
||||
</span>
|
||||
<span style={{ float: 'right' }}>
|
||||
<Popconfirm
|
||||
title="Delete the task"
|
||||
description="Are you sure to delete this task?"
|
||||
onConfirm={(e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation()
|
||||
console.log(confirm)
|
||||
confirm(item.chunk_id)
|
||||
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation()
|
||||
}} />
|
||||
</Popconfirm>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Card>
|
||||
</Col>)
|
||||
})
|
||||
}
|
||||
</Row>
|
||||
</Spin>
|
||||
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
<div className={styles.pageFooter}>
|
||||
<Pagination
|
||||
responsive
|
||||
showLessItems
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
onChange={onShowSizeChange}
|
||||
defaultPageSize={30}
|
||||
pageSizeOptions={[30, 60, 90]}
|
||||
defaultCurrent={pagination.page}
|
||||
total={total}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.pageFooter}>
|
||||
<Pagination
|
||||
responsive
|
||||
showLessItems
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
onChange={onShowSizeChange}
|
||||
defaultPageSize={30}
|
||||
pageSizeOptions={[30, 60, 90]}
|
||||
defaultCurrent={pagination.page}
|
||||
total={total}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div >
|
||||
<CreateModal doc_id={doc_id} isShowCreateModal={isShowCreateModal} chunk_id={chunk_id} getChunkList={getChunkList} />
|
||||
</>
|
||||
)
|
||||
<CreateModal
|
||||
doc_id={doc_id}
|
||||
isShowCreateModal={isShowCreateModal}
|
||||
chunk_id={chunk_id}
|
||||
getChunkList={getChunkList}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ chunkModel, loading }) => ({ chunkModel, loading }))(Index);
|
||||
export default Chunk;
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import kbService from '@/services/kbService';
|
||||
import { Effect, Reducer } from 'umi';
|
||||
import { DvaModel } from 'umi';
|
||||
|
||||
export interface chunkModelState {
|
||||
loading: boolean;
|
||||
export interface ChunkModelState {
|
||||
data: any[];
|
||||
total: number;
|
||||
isShowCreateModal: boolean;
|
||||
@ -10,25 +9,10 @@ export interface chunkModelState {
|
||||
doc_id: string;
|
||||
chunkInfo: any;
|
||||
}
|
||||
export interface chunkgModelType {
|
||||
namespace: 'chunkModel';
|
||||
state: chunkModelState;
|
||||
effects: {
|
||||
chunk_list: Effect;
|
||||
get_chunk: Effect;
|
||||
create_hunk: Effect;
|
||||
switch_chunk: Effect;
|
||||
rm_chunk: Effect;
|
||||
};
|
||||
reducers: {
|
||||
updateState: Reducer<chunkModelState>;
|
||||
};
|
||||
// subscriptions: { setup: Subscription };
|
||||
}
|
||||
const Model: chunkgModelType = {
|
||||
|
||||
const model: DvaModel<ChunkModelState> = {
|
||||
namespace: 'chunkModel',
|
||||
state: {
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
isShowCreateModal: false,
|
||||
@ -36,6 +20,14 @@ const Model: chunkgModelType = {
|
||||
doc_id: '',
|
||||
chunkInfo: {},
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
// subscriptions: {
|
||||
// setup({ dispatch, history }) {
|
||||
// history.listen(location => {
|
||||
@ -44,7 +36,7 @@ const Model: chunkgModelType = {
|
||||
// }
|
||||
// },
|
||||
effects: {
|
||||
*chunk_list({ payload = {}, callback }, { call, put }) {
|
||||
*chunk_list({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.chunk_list, payload);
|
||||
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
@ -55,28 +47,23 @@ const Model: chunkgModelType = {
|
||||
payload: {
|
||||
data: res.chunks,
|
||||
total: res.total,
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
callback && callback();
|
||||
}
|
||||
},
|
||||
*switch_chunk({ payload = {}, callback }, { call, put }) {
|
||||
*switch_chunk({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.switch_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
callback && callback();
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
*rm_chunk({ payload = {}, callback }, { call, put }) {
|
||||
*rm_chunk({ payload = {} }, { call, put }) {
|
||||
console.log('shanchu');
|
||||
const { data, response } = yield call(kbService.rm_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
return retcode;
|
||||
},
|
||||
*get_chunk({ payload = {}, callback }, { call, put }) {
|
||||
*get_chunk({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.get_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
@ -86,28 +73,16 @@ const Model: chunkgModelType = {
|
||||
chunkInfo: res,
|
||||
},
|
||||
});
|
||||
callback && callback(res);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
*create_hunk({ payload = {} }, { call, put }) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: true,
|
||||
},
|
||||
});
|
||||
let service = kbService.create_chunk;
|
||||
if (payload.chunk_id) {
|
||||
service = kbService.set_chunk;
|
||||
}
|
||||
const { data, response } = yield call(service, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
@ -118,13 +93,5 @@ const Model: chunkgModelType = {
|
||||
}
|
||||
},
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
export default Model;
|
||||
export default model;
|
||||
|
||||
@ -1,79 +1,73 @@
|
||||
import React from 'react'
|
||||
import { connect, Dispatch } from 'umi';
|
||||
import i18n from 'i18next';
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import { Input, Modal, Form } from 'antd'
|
||||
import styles from './index.less';
|
||||
import type { kFModelState } from './model'
|
||||
import { Form, Input, Modal } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
|
||||
type FieldType = {
|
||||
name?: string;
|
||||
name?: string;
|
||||
};
|
||||
interface kFProps {
|
||||
dispatch: Dispatch;
|
||||
kFModel: kFModelState;
|
||||
getKfList: () => void;
|
||||
kb_id: string
|
||||
getKfList: () => void;
|
||||
kb_id: string;
|
||||
}
|
||||
const Index: React.FC<kFProps> = ({ kFModel, dispatch, getKfList, kb_id }) => {
|
||||
const { isShowCEFwModal } = kFModel
|
||||
const { t } = useTranslation()
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowCEFwModal: false
|
||||
}
|
||||
});
|
||||
};
|
||||
const [form] = Form.useForm()
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
dispatch({
|
||||
type: 'kFModel/document_create',
|
||||
payload: {
|
||||
name: values.name,
|
||||
kb_id
|
||||
},
|
||||
callback: () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowCEFwModal: false
|
||||
}
|
||||
});
|
||||
getKfList && getKfList()
|
||||
}
|
||||
});
|
||||
|
||||
} catch (errorInfo) {
|
||||
console.log('Failed:', errorInfo);
|
||||
}
|
||||
};
|
||||
const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => {
|
||||
const dispatch = useDispatch();
|
||||
const kFModel = useSelector((state: any) => state.kFModel);
|
||||
const { isShowCEFwModal } = kFModel;
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Modal title="Basic Modal" open={isShowCEFwModal} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Form
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="文件名"
|
||||
name="name"
|
||||
rules={[{ required: true, message: 'Please input value!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowCEFwModal: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const retcode = await dispatch<any>({
|
||||
type: 'kFModel/document_create',
|
||||
payload: {
|
||||
name: values.name,
|
||||
kb_id,
|
||||
},
|
||||
});
|
||||
if (retcode === 0) {
|
||||
getKfList && getKfList();
|
||||
}
|
||||
} catch (errorInfo) {
|
||||
console.log('Failed:', errorInfo);
|
||||
}
|
||||
};
|
||||
|
||||
</Form>
|
||||
</Modal >
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
export default connect(({ kFModel, loading }) => ({ kFModel, loading }))(Index);
|
||||
return (
|
||||
<Modal
|
||||
title="Basic Modal"
|
||||
open={isShowCEFwModal}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
style={{ maxWidth: 600 }}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<FieldType>
|
||||
label="文件名"
|
||||
name="name"
|
||||
rules={[{ required: true, message: 'Please input value!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
export default FileCreatingModal;
|
||||
|
||||
@ -1,228 +1,273 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { connect, Dispatch, useNavigate } from 'umi'
|
||||
import { Space, Table, Input, Button, Switch, Dropdown, } from 'antd';
|
||||
import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { DownOutlined } from '@ant-design/icons'
|
||||
import { debounce } from 'lodash';
|
||||
import { Button, Dropdown, Input, Space, Switch, Table } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import UploadFile from './upload'
|
||||
import CreateEPModal from './createEFileModal'
|
||||
import SegmentSetModal from './segmentSetModal'
|
||||
import styles from './index.less'
|
||||
import type { kFModelState } from './model'
|
||||
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 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
|
||||
name: string;
|
||||
chunk_num: string;
|
||||
token_num: number;
|
||||
update_date: string;
|
||||
size: string;
|
||||
status: string;
|
||||
id: string;
|
||||
parser_id: string;
|
||||
}
|
||||
|
||||
interface kFProps {
|
||||
dispatch: Dispatch;
|
||||
kFModel: kFModelState;
|
||||
kb_id: string
|
||||
interface KFProps {
|
||||
kb_id: string;
|
||||
}
|
||||
|
||||
const Index: React.FC<kFProps> = ({ kFModel, dispatch, kb_id }) => {
|
||||
const { data, loading } = kFModel
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [doc_id, setDocId] = useState('0')
|
||||
const [parser_id, setParserId] = useState('0')
|
||||
let navigate = useNavigate();
|
||||
const getKfList = (keywords?: string) => {
|
||||
const payload = {
|
||||
kb_id,
|
||||
keywords
|
||||
}
|
||||
if (!keywords) {
|
||||
delete payload.keywords
|
||||
}
|
||||
dispatch({
|
||||
type: 'kFModel/getKfList',
|
||||
payload
|
||||
});
|
||||
}
|
||||
useEffect(() => {
|
||||
if (kb_id) {
|
||||
getKfList()
|
||||
}
|
||||
}, [kb_id])
|
||||
const debounceChange = debounce(getKfList, 300)
|
||||
const debounceCallback = useCallback((value: string) => debounceChange(value), [])
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const value = e.target.value
|
||||
setInputValue(value)
|
||||
debounceCallback(e.target.value)
|
||||
const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => {
|
||||
const dispatch = useDispatch();
|
||||
const kFModel = useSelector((state: any) => state.kFModel);
|
||||
const effects = useSelector((state: any) => state.loading.effects);
|
||||
const { data } = kFModel;
|
||||
const loading = getOneNamespaceEffectsLoading('kFModel', effects, [
|
||||
'getKfList',
|
||||
'updateDocumentStatus',
|
||||
]);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [doc_id, setDocId] = useState('0');
|
||||
const [parser_id, setParserId] = useState('0');
|
||||
let navigate = useNavigate();
|
||||
|
||||
}
|
||||
const onChangeStatus = (e: boolean, doc_id: string) => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateDocumentStatus',
|
||||
payload: {
|
||||
doc_id,
|
||||
status: Number(e)
|
||||
},
|
||||
callback() {
|
||||
getKfList()
|
||||
}
|
||||
});
|
||||
}
|
||||
const onRmDocument = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/document_rm',
|
||||
payload: {
|
||||
doc_id
|
||||
},
|
||||
callback() {
|
||||
getKfList()
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
const showCEFModal = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowCEFwModal: true
|
||||
}
|
||||
});
|
||||
const getKfList = (keywords?: string) => {
|
||||
const payload = {
|
||||
kb_id,
|
||||
keywords,
|
||||
};
|
||||
|
||||
const showSegmentSetModal = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: true
|
||||
}
|
||||
});
|
||||
};
|
||||
const actionItems: MenuProps['items'] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<div>
|
||||
<UploadFile kb_id={kb_id} getKfList={getKfList} />
|
||||
</div>
|
||||
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={showCEFModal}> 导入虚拟文件</Button>
|
||||
</div>
|
||||
),
|
||||
// disabled: true,
|
||||
},
|
||||
]
|
||||
}, [kb_id]);
|
||||
const chunkItems: MenuProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<div>
|
||||
|
||||
<Button type="link" onClick={showSegmentSetModal}> 分段设置</Button>
|
||||
</div>
|
||||
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={onRmDocument}> 删除</Button>
|
||||
</div>
|
||||
),
|
||||
// disabled: true,
|
||||
},
|
||||
]
|
||||
const toChunk = (id: string) => {
|
||||
console.log(id)
|
||||
navigate(`/knowledge/add/setting?activeKey=file&id=${kb_id}&doc_id=${id}`);
|
||||
if (!keywords) {
|
||||
delete payload.keywords;
|
||||
}
|
||||
const columns: ColumnsType<DataType> = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text: any, { id }) => <div className={styles.tochunks} onClick={() => toChunk(id)}><img className={styles.img} src='https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg' alt="" />{text}</div>,
|
||||
className: `${styles.column}`
|
||||
},
|
||||
{
|
||||
title: '数据总量',
|
||||
dataIndex: 'chunk_num',
|
||||
key: 'chunk_num',
|
||||
className: `${styles.column}`
|
||||
},
|
||||
{
|
||||
title: 'Tokens',
|
||||
dataIndex: 'token_num',
|
||||
key: 'token_num',
|
||||
className: `${styles.column}`
|
||||
},
|
||||
{
|
||||
title: '文件大小',
|
||||
dataIndex: 'size',
|
||||
key: 'size',
|
||||
className: `${styles.column}`
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
dataIndex: 'status',
|
||||
className: `${styles.column}`,
|
||||
render: (_, { status: string, id }) => (
|
||||
<>
|
||||
<Switch defaultChecked={status === '1'} onChange={(e) => {
|
||||
onChangeStatus(e, 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>
|
||||
),
|
||||
},
|
||||
dispatch({
|
||||
type: 'kFModel/getKfList',
|
||||
payload,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (kb_id) {
|
||||
getKfList();
|
||||
}
|
||||
}, [kb_id]);
|
||||
|
||||
const debounceChange = debounce(getKfList, 300);
|
||||
const debounceCallback = useCallback(
|
||||
(value: string) => debounceChange(value),
|
||||
[],
|
||||
);
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value);
|
||||
debounceCallback(e.target.value);
|
||||
};
|
||||
const onChangeStatus = (e: boolean, doc_id: string) => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateDocumentStatus',
|
||||
payload: {
|
||||
doc_id,
|
||||
status: Number(e),
|
||||
kb_id,
|
||||
},
|
||||
});
|
||||
};
|
||||
const onRmDocument = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/document_rm',
|
||||
payload: {
|
||||
doc_id,
|
||||
kb_id,
|
||||
},
|
||||
});
|
||||
};
|
||||
const showCEFModal = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowCEFwModal: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const showSegmentSetModal = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
const actionItems: MenuProps['items'] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<div>
|
||||
<UploadFile kb_id={kb_id} getKfList={getKfList} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={showCEFModal}>
|
||||
{' '}
|
||||
导入虚拟文件
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
// disabled: true,
|
||||
},
|
||||
];
|
||||
return <>
|
||||
<div className={styles.filter}>
|
||||
<div className="search">
|
||||
<Input placeholder="搜索" value={inputValue} style={{ width: 220 }} allowClear onChange={handleInputChange} />
|
||||
</div>
|
||||
<div className="operate">
|
||||
<Dropdown menu={{ items: actionItems }} trigger={['click']} >
|
||||
<a>
|
||||
导入文件 <DownOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
|
||||
</div>
|
||||
}, [kb_id]);
|
||||
const chunkItems: MenuProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={showSegmentSetModal}>
|
||||
{' '}
|
||||
分段设置
|
||||
</Button>
|
||||
</div>
|
||||
<Table rowKey='id' columns={columns} dataSource={data} loading={loading} pagination={false} scroll={{ scrollToFirstRowOnChange: true, x: true }} />
|
||||
<CreateEPModal getKfList={getKfList} kb_id={kb_id} />
|
||||
<SegmentSetModal getKfList={getKfList} parser_id={parser_id} doc_id={doc_id} />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={onRmDocument}>
|
||||
{' '}
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
// disabled: true,
|
||||
},
|
||||
];
|
||||
const toChunk = (id: string) => {
|
||||
console.log(id);
|
||||
navigate(`/knowledge/add/setting?activeKey=file&id=${kb_id}&doc_id=${id}`);
|
||||
};
|
||||
const columns: ColumnsType<DataType> = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text: any, { id }) => (
|
||||
<div className={styles.tochunks} onClick={() => toChunk(id)}>
|
||||
<img
|
||||
className={styles.img}
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg"
|
||||
alt=""
|
||||
/>
|
||||
{text}
|
||||
</div>
|
||||
),
|
||||
className: `${styles.column}`,
|
||||
},
|
||||
{
|
||||
title: '数据总量',
|
||||
dataIndex: 'chunk_num',
|
||||
key: 'chunk_num',
|
||||
className: `${styles.column}`,
|
||||
},
|
||||
{
|
||||
title: 'Tokens',
|
||||
dataIndex: 'token_num',
|
||||
key: 'token_num',
|
||||
className: `${styles.column}`,
|
||||
},
|
||||
{
|
||||
title: '文件大小',
|
||||
dataIndex: 'size',
|
||||
key: 'size',
|
||||
className: `${styles.column}`,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
dataIndex: 'status',
|
||||
className: `${styles.column}`,
|
||||
render: (_, { status: string, id }) => (
|
||||
<>
|
||||
<Switch
|
||||
defaultChecked={status === '1'}
|
||||
onChange={(e) => {
|
||||
onChangeStatus(e, 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>
|
||||
),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<div className={styles.filter}>
|
||||
<div className="search">
|
||||
<Input
|
||||
placeholder="搜索"
|
||||
value={inputValue}
|
||||
style={{ width: 220 }}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="operate">
|
||||
<Dropdown menu={{ items: actionItems }} trigger={['click']}>
|
||||
<a>
|
||||
导入文件 <DownOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
scroll={{ scrollToFirstRowOnChange: true, x: true }}
|
||||
/>
|
||||
<CreateEPModal getKfList={getKfList} kb_id={kb_id} />
|
||||
<SegmentSetModal
|
||||
getKfList={getKfList}
|
||||
parser_id={parser_id}
|
||||
doc_id={doc_id}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ kFModel, loading }) => ({ kFModel, loading }))(Index);
|
||||
export default KnowledgeFile;
|
||||
|
||||
@ -1,57 +1,47 @@
|
||||
import kbService from '@/services/kbService';
|
||||
import { message } from 'antd';
|
||||
import { Effect, Reducer, Subscription } from 'umi';
|
||||
import pick from 'lodash/pick';
|
||||
import { DvaModel } from 'umi';
|
||||
|
||||
export interface kFModelState {
|
||||
export interface KFModelState {
|
||||
isShowCEFwModal: boolean;
|
||||
isShowTntModal: boolean;
|
||||
isShowSegmentSetModal: boolean;
|
||||
loading: boolean;
|
||||
tenantIfo: any;
|
||||
data: any[];
|
||||
}
|
||||
export interface kFModelType {
|
||||
namespace: 'kFModel';
|
||||
state: kFModelState;
|
||||
effects: {
|
||||
createKf: Effect;
|
||||
updateKf: Effect;
|
||||
getKfDetail: Effect;
|
||||
getKfList: Effect;
|
||||
updateDocumentStatus: Effect;
|
||||
document_rm: Effect;
|
||||
document_create: Effect;
|
||||
document_change_parser: Effect;
|
||||
};
|
||||
reducers: {
|
||||
updateState: Reducer<kFModelState>;
|
||||
};
|
||||
subscriptions: { setup: Subscription };
|
||||
}
|
||||
const Model: kFModelType = {
|
||||
|
||||
const model: DvaModel<KFModelState> = {
|
||||
namespace: 'kFModel',
|
||||
state: {
|
||||
isShowCEFwModal: false,
|
||||
isShowTntModal: false,
|
||||
isShowSegmentSetModal: false,
|
||||
loading: false,
|
||||
tenantIfo: {},
|
||||
data: [],
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen((location) => {});
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
*createKf({ payload = {}, callback }, { call, put }) {
|
||||
*createKf({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.createKb, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('创建成功!');
|
||||
}
|
||||
},
|
||||
*updateKf({ payload = {}, callback }, { call, put }) {
|
||||
*updateKf({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.updateKb, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
@ -67,23 +57,12 @@ const Model: kFModelType = {
|
||||
}
|
||||
},
|
||||
*getKfList({ payload = {} }, { call, put }) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: true,
|
||||
},
|
||||
});
|
||||
const { data, response } = yield call(
|
||||
kbService.get_document_list,
|
||||
payload,
|
||||
);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
@ -93,64 +72,64 @@ const Model: kFModelType = {
|
||||
});
|
||||
}
|
||||
},
|
||||
*updateDocumentStatus({ payload = {}, callback }, { call, put }) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: true,
|
||||
},
|
||||
});
|
||||
*updateDocumentStatus({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(
|
||||
kbService.document_change_status,
|
||||
payload,
|
||||
pick(payload, ['doc_id', 'status']),
|
||||
);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('修改成功!');
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
},
|
||||
put({
|
||||
type: 'getKfList',
|
||||
payload: { kb_id: payload.kb_id },
|
||||
});
|
||||
callback && callback();
|
||||
}
|
||||
},
|
||||
*document_rm({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.document_rm, payload);
|
||||
*document_rm({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.document_rm, {
|
||||
doc_id: payload.doc_id,
|
||||
});
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('删除成功!');
|
||||
callback && callback();
|
||||
put({
|
||||
type: 'getKfList',
|
||||
payload: { kb_id: payload.kb_id },
|
||||
});
|
||||
}
|
||||
},
|
||||
*document_create({ payload = {}, callback }, { call, put }) {
|
||||
*document_create({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.document_create, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
put({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowCEFwModal: false,
|
||||
},
|
||||
});
|
||||
message.success('创建成功!');
|
||||
callback && callback();
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
*document_change_parser({ payload = {}, callback }, { call, put }) {
|
||||
*document_change_parser({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(
|
||||
kbService.document_change_parser,
|
||||
payload,
|
||||
);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: false,
|
||||
},
|
||||
});
|
||||
message.success('修改成功!');
|
||||
callback && callback();
|
||||
}
|
||||
},
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
return retcode;
|
||||
},
|
||||
},
|
||||
};
|
||||
export default Model;
|
||||
export default model;
|
||||
|
||||
@ -1,91 +1,87 @@
|
||||
import React from 'react';
|
||||
import { connect, Dispatch } from 'umi';
|
||||
import i18n from 'i18next';
|
||||
import { useTranslation, } from 'react-i18next'
|
||||
import { Modal, Tag, Space } from 'antd'
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Modal, Space, Tag } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
import styles from './index.less';
|
||||
import type { kFModelState } from './model'
|
||||
import type { settingModelState } from '@/pages/setting/model'
|
||||
const { CheckableTag } = Tag;
|
||||
interface kFProps {
|
||||
dispatch: Dispatch;
|
||||
kFModel: kFModelState;
|
||||
settingModel: settingModelState;
|
||||
getKfList: () => void;
|
||||
parser_id: string;
|
||||
doc_id: string;
|
||||
getKfList: () => void;
|
||||
parser_id: string;
|
||||
doc_id: string;
|
||||
}
|
||||
const Index: React.FC<kFProps> = ({ kFModel, settingModel, dispatch, getKfList, parser_id, doc_id }) => {
|
||||
const [selectedTag, setSelectedTag] = useState('')
|
||||
const { tenantIfo = {} } = settingModel
|
||||
const { parser_ids = '' } = tenantIfo
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'settingModel/getTenantInfo',
|
||||
payload: {
|
||||
}
|
||||
});
|
||||
setSelectedTag(parser_id)
|
||||
}, [parser_id])
|
||||
const { isShowSegmentSetModal } = kFModel
|
||||
const { t } = useTranslation()
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: false
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleOk = () => {
|
||||
console.log(1111, selectedTag)
|
||||
dispatch({
|
||||
type: 'kFModel/document_change_parser',
|
||||
payload: {
|
||||
parser_id: selectedTag,
|
||||
doc_id
|
||||
},
|
||||
callback: () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: false
|
||||
}
|
||||
});
|
||||
getKfList && getKfList()
|
||||
}
|
||||
});
|
||||
};
|
||||
const SegmentSetModal: React.FC<kFProps> = ({
|
||||
getKfList,
|
||||
parser_id,
|
||||
doc_id,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const kFModel = useSelector((state: any) => state.kFModel);
|
||||
const settingModel = useSelector((state: any) => state.settingModel);
|
||||
const [selectedTag, setSelectedTag] = useState('');
|
||||
const { tenantIfo = {} } = settingModel;
|
||||
const { parser_ids = '' } = tenantIfo;
|
||||
const { isShowSegmentSetModal } = kFModel;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = (tag: string, checked: boolean) => {
|
||||
const nextSelectedTag = checked
|
||||
? tag
|
||||
: selectedTag;
|
||||
console.log('You are interested in: ', nextSelectedTag);
|
||||
setSelectedTag(nextSelectedTag);
|
||||
};
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'settingModel/getTenantInfo',
|
||||
payload: {},
|
||||
});
|
||||
setSelectedTag(parser_id);
|
||||
}, [parser_id]);
|
||||
|
||||
return (
|
||||
<Modal title="Basic Modal" open={isShowSegmentSetModal} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Space size={[0, 8]} wrap>
|
||||
<div className={styles.tags}>
|
||||
{
|
||||
parser_ids.split(',').map((tag: string) => {
|
||||
return (<CheckableTag
|
||||
key={tag}
|
||||
checked={selectedTag === tag}
|
||||
onChange={(checked) => handleChange(tag, checked)}
|
||||
>
|
||||
{tag}
|
||||
</CheckableTag>)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</Space>
|
||||
</Modal >
|
||||
const handleCancel = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
console.log(1111, selectedTag);
|
||||
const retcode = await dispatch<any>({
|
||||
type: 'kFModel/document_change_parser',
|
||||
payload: {
|
||||
parser_id: selectedTag,
|
||||
doc_id,
|
||||
},
|
||||
});
|
||||
|
||||
);
|
||||
}
|
||||
export default connect(({ kFModel, settingModel, loading }) => ({ kFModel, settingModel, loading }))(Index);
|
||||
retcode === 0 && getKfList && getKfList();
|
||||
};
|
||||
|
||||
const handleChange = (tag: string, checked: boolean) => {
|
||||
const nextSelectedTag = checked ? tag : selectedTag;
|
||||
console.log('You are interested in: ', nextSelectedTag);
|
||||
setSelectedTag(nextSelectedTag);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Basic Modal"
|
||||
open={isShowSegmentSetModal}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Space size={[0, 8]} wrap>
|
||||
<div className={styles.tags}>
|
||||
{parser_ids.split(',').map((tag: string) => {
|
||||
return (
|
||||
<CheckableTag
|
||||
key={tag}
|
||||
checked={selectedTag === tag}
|
||||
onChange={(checked) => handleChange(tag, checked)}
|
||||
>
|
||||
{tag}
|
||||
</CheckableTag>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Space>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
export default SegmentSetModal;
|
||||
|
||||
@ -1,33 +1,39 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'umi'
|
||||
import uploadService from '@/services/uploadService';
|
||||
import type { UploadProps } from 'antd';
|
||||
import { Button, Upload } from 'antd';
|
||||
import uploadService from '@/services/uploadService'
|
||||
import React from 'react';
|
||||
interface PropsType {
|
||||
kb_id: string;
|
||||
getKfList: () => void
|
||||
kb_id: string;
|
||||
getKfList: () => void;
|
||||
}
|
||||
|
||||
type UploadRequestOption = Parameters<
|
||||
NonNullable<UploadProps["customRequest"]>
|
||||
NonNullable<UploadProps['customRequest']>
|
||||
>[0];
|
||||
const Index: React.FC<PropsType> = ({ kb_id, getKfList }) => {
|
||||
const createRequest: (props: UploadRequestOption) => void = async function ({ file, onSuccess, onError }) {
|
||||
const { retcode, data } = await uploadService.uploadFile(file, kb_id);
|
||||
if (retcode === 0) {
|
||||
onSuccess && onSuccess(data, file);
|
||||
|
||||
} else {
|
||||
onError && onError(data);
|
||||
}
|
||||
getKfList && getKfList()
|
||||
};
|
||||
const uploadProps: UploadProps = {
|
||||
customRequest: createRequest,
|
||||
showUploadList: false,
|
||||
};
|
||||
return (<Upload {...uploadProps} >
|
||||
<Button type="link">导入文件</Button>
|
||||
</Upload>)
|
||||
}
|
||||
const FileUpload: React.FC<PropsType> = ({ kb_id, getKfList }) => {
|
||||
const createRequest: (props: UploadRequestOption) => void = async function ({
|
||||
file,
|
||||
onSuccess,
|
||||
onError,
|
||||
}) {
|
||||
const { retcode, data } = await uploadService.uploadFile(file, kb_id);
|
||||
if (retcode === 0) {
|
||||
onSuccess && onSuccess(data, file);
|
||||
} else {
|
||||
onError && onError(data);
|
||||
}
|
||||
getKfList && getKfList();
|
||||
};
|
||||
const uploadProps: UploadProps = {
|
||||
customRequest: createRequest,
|
||||
showUploadList: false,
|
||||
};
|
||||
return (
|
||||
<Upload {...uploadProps}>
|
||||
<Button type="link">导入文件</Button>
|
||||
</Upload>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ kFModel, settingModel, loading }) => ({ kFModel, settingModel, loading }))(Index);
|
||||
export default FileUpload;
|
||||
|
||||
@ -1,247 +1,278 @@
|
||||
import React, { useEffect, useState, useCallback, } from 'react';
|
||||
import { useNavigate, connect, Dispatch } from 'umi'
|
||||
import { Card, Row, Col, Input, Select, Switch, Pagination, Spin, Button, Popconfirm } from 'antd';
|
||||
import { MinusSquareOutlined, DeleteOutlined, } from '@ant-design/icons';
|
||||
import { api_host } from '@/utils/api';
|
||||
import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons';
|
||||
import type { PaginationProps } from 'antd';
|
||||
import { api_host } from '@/utils/api'
|
||||
import CreateModal from '../knowledge-chunk/components/createModal'
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
Input,
|
||||
Pagination,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Select,
|
||||
Spin,
|
||||
Switch,
|
||||
} from 'antd';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
import CreateModal from '../knowledge-chunk/components/createModal';
|
||||
|
||||
|
||||
import styles from './index.less'
|
||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { debounce } from 'lodash';
|
||||
import type { kSearchModelState } from './model'
|
||||
import type { chunkModelState } from '../knowledge-chunk/model'
|
||||
import styles from './index.less';
|
||||
interface chunkProps {
|
||||
dispatch: Dispatch;
|
||||
kSearchModel: kSearchModelState;
|
||||
chunkModel: chunkModelState;
|
||||
kb_id: string
|
||||
kb_id: string;
|
||||
}
|
||||
const Index: React.FC<chunkProps> = ({ kSearchModel, chunkModel, dispatch, kb_id }) => {
|
||||
|
||||
const { data = [], total, loading, d_list = [], question, doc_ids, pagination, } = kSearchModel
|
||||
const { chunk_id, doc_id, isShowCreateModal } = chunkModel
|
||||
const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => {
|
||||
const dispatch = useDispatch();
|
||||
const kSearchModel = useSelector((state: any) => state.kSearchModel);
|
||||
const chunkModel = useSelector((state: any) => state.chunkModel);
|
||||
const loading = useOneNamespaceEffectsLoading('kSearchModel', [
|
||||
'chunk_list',
|
||||
'switch_chunk',
|
||||
]);
|
||||
|
||||
const {
|
||||
data = [],
|
||||
total,
|
||||
d_list = [],
|
||||
question,
|
||||
doc_ids,
|
||||
pagination,
|
||||
} = kSearchModel;
|
||||
const { chunk_id, doc_id, isShowCreateModal } = chunkModel;
|
||||
|
||||
const getChunkList = () => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
interface payloadType {
|
||||
kb_id: string;
|
||||
question?: string;
|
||||
doc_ids: any[];
|
||||
similarity_threshold?: number
|
||||
}
|
||||
const payload: payloadType = {
|
||||
kb_id,
|
||||
question,
|
||||
doc_ids,
|
||||
similarity_threshold: 0.1
|
||||
}
|
||||
dispatch({
|
||||
type: 'kSearchModel/chunk_list',
|
||||
payload: {
|
||||
...payload,
|
||||
...pagination
|
||||
}
|
||||
kb_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
const confirm = (id: string) => {
|
||||
console.log(id)
|
||||
dispatch({
|
||||
type: 'kSearchModel/rm_chunk',
|
||||
payload: {
|
||||
chunk_ids: [id]
|
||||
chunk_ids: [id],
|
||||
kb_id,
|
||||
},
|
||||
callback: getChunkList
|
||||
});
|
||||
};
|
||||
const handleEditchunk = (item: any) => {
|
||||
const { chunk_id, doc_id } = item
|
||||
const { chunk_id, doc_id } = item;
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
isShowCreateModal: true,
|
||||
chunk_id,
|
||||
doc_id
|
||||
doc_id,
|
||||
},
|
||||
callback: getChunkList
|
||||
});
|
||||
}
|
||||
const onShowSizeChange: PaginationProps['onShowSizeChange'] = (page, size) => {
|
||||
getChunkList();
|
||||
};
|
||||
const onShowSizeChange: PaginationProps['onShowSizeChange'] = (
|
||||
page,
|
||||
size,
|
||||
) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
pagination: { page, size }
|
||||
}
|
||||
pagination: { page, size },
|
||||
},
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
doc_ids: [],
|
||||
question: ""
|
||||
}
|
||||
question: '',
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: 'kSearchModel/getKfList',
|
||||
payload: {
|
||||
kb_id
|
||||
}
|
||||
|
||||
kb_id,
|
||||
},
|
||||
});
|
||||
}, [])
|
||||
}, []);
|
||||
const switchChunk = (item: any, available_int: boolean) => {
|
||||
const { chunk_id, doc_id } = item
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
const { chunk_id, doc_id } = item;
|
||||
|
||||
dispatch({
|
||||
type: 'kSearchModel/switch_chunk',
|
||||
payload: {
|
||||
chunk_ids: [chunk_id],
|
||||
doc_id,
|
||||
available_int
|
||||
available_int,
|
||||
kb_id,
|
||||
},
|
||||
callback: getChunkList
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getChunkList()
|
||||
}, [doc_ids, pagination, question])
|
||||
getChunkList();
|
||||
}, [doc_ids, pagination, question]);
|
||||
const debounceChange = debounce((value) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
question: value
|
||||
}
|
||||
question: value,
|
||||
},
|
||||
});
|
||||
}, 300)
|
||||
const debounceCallback = useCallback((value: string) => debounceChange(value), [])
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const value = e.target.value
|
||||
debounceCallback(value)
|
||||
}
|
||||
const handleSelectChange = (value:
|
||||
any[]) => {
|
||||
}, 300);
|
||||
|
||||
const debounceCallback = useCallback(
|
||||
(value: string) => debounceChange(value),
|
||||
[],
|
||||
);
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const value = e.target.value;
|
||||
debounceCallback(value);
|
||||
};
|
||||
const handleSelectChange = (value: any[]) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
doc_ids: value
|
||||
}
|
||||
doc_ids: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log('loading', loading)
|
||||
return (<>
|
||||
<div className={styles.chunkPage}>
|
||||
<div className={styles.filter}>
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="文件列表"
|
||||
optionFilterProp="children"
|
||||
onChange={handleSelectChange}
|
||||
style={{ width: 300, marginBottom: 20 }}
|
||||
options={d_list}
|
||||
fieldNames={{ label: 'name', value: 'id' }}
|
||||
mode='multiple'
|
||||
/>
|
||||
};
|
||||
|
||||
<Input.TextArea autoSize={{ minRows: 6, maxRows: 6 }} placeholder="搜索" style={{ width: 300 }} allowClear onChange={handleInputChange} />
|
||||
return (
|
||||
<>
|
||||
<div className={styles.chunkPage}>
|
||||
<div className={styles.filter}>
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="文件列表"
|
||||
optionFilterProp="children"
|
||||
onChange={handleSelectChange}
|
||||
style={{ width: 300, marginBottom: 20 }}
|
||||
options={d_list}
|
||||
fieldNames={{ label: 'name', value: 'id' }}
|
||||
mode="multiple"
|
||||
/>
|
||||
|
||||
</div>
|
||||
<div className={styles.pageContainer}>
|
||||
<div className={styles.pageContent}>
|
||||
<Spin spinning={loading} className={styles.spin} size='large'>
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24 }} >
|
||||
{
|
||||
data.map((item: any) => {
|
||||
return (<Col className="gutter-row" key={item.chunk_id} xs={24} sm={12} md={12} lg={8}>
|
||||
<Card className={styles.card}
|
||||
onClick={() => { handleEditchunk(item) }}
|
||||
>
|
||||
<img style={{ width: '50px' }} src={`${api_host}/document/image/${item.img_id}`} alt="" />
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.context}>
|
||||
{item.content_ltks}
|
||||
</span>
|
||||
<span className={styles.delete}>
|
||||
<Switch size="small" defaultValue={item.doc_ids == '1'} onChange={(checked: boolean, e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation(); switchChunk(item, checked)
|
||||
}} />
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />{item.doc_num}文档
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />{item.chunk_num}个
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />{item.token_num}千字符
|
||||
</span>
|
||||
<span style={{ float: 'right' }}>
|
||||
<Popconfirm
|
||||
title="Delete the task"
|
||||
description="Are you sure to delete this task?"
|
||||
onConfirm={(e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation()
|
||||
console.log(confirm)
|
||||
confirm(item.chunk_id)
|
||||
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation()
|
||||
}} />
|
||||
</Popconfirm>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Card>
|
||||
</Col>)
|
||||
})
|
||||
}
|
||||
</Row>
|
||||
</Spin>
|
||||
|
||||
</div>
|
||||
<div className={styles.pageFooter}>
|
||||
<Pagination
|
||||
responsive
|
||||
showLessItems
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
onChange={onShowSizeChange}
|
||||
defaultPageSize={30}
|
||||
pageSizeOptions={[30, 60, 90]}
|
||||
defaultCurrent={pagination.page}
|
||||
total={total}
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 6, maxRows: 6 }}
|
||||
placeholder="搜索"
|
||||
style={{ width: 300 }}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.pageContainer}>
|
||||
<div className={styles.pageContent}>
|
||||
<Spin spinning={loading} className={styles.spin} size="large">
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24 }}>
|
||||
{data.map((item: any) => {
|
||||
return (
|
||||
<Col
|
||||
className="gutter-row"
|
||||
key={item.chunk_id}
|
||||
xs={24}
|
||||
sm={12}
|
||||
md={12}
|
||||
lg={8}
|
||||
>
|
||||
<Card
|
||||
className={styles.card}
|
||||
onClick={() => {
|
||||
handleEditchunk(item);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{ width: '50px' }}
|
||||
src={`${api_host}/document/image/${item.img_id}`}
|
||||
alt=""
|
||||
/>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.context}>
|
||||
{item.content_ltks}
|
||||
</span>
|
||||
<span className={styles.delete}>
|
||||
<Switch
|
||||
size="small"
|
||||
defaultValue={item.doc_ids == '1'}
|
||||
onChange={(checked: boolean, e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
switchChunk(item, checked);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.doc_num}文档
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.chunk_num}个
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.token_num}千字符
|
||||
</span>
|
||||
<span style={{ float: 'right' }}>
|
||||
<Popconfirm
|
||||
title="Delete the task"
|
||||
description="Are you sure to delete this task?"
|
||||
onConfirm={(e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
console.log(confirm);
|
||||
confirm(item.chunk_id);
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
}}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
<div className={styles.pageFooter}>
|
||||
<Pagination
|
||||
responsive
|
||||
showLessItems
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
onChange={onShowSizeChange}
|
||||
defaultPageSize={30}
|
||||
pageSizeOptions={[30, 60, 90]}
|
||||
defaultCurrent={pagination.page}
|
||||
total={total}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div >
|
||||
<CreateModal getChunkList={getChunkList} isShowCreateModal={isShowCreateModal} chunk_id={chunk_id} doc_id={doc_id} />
|
||||
</>
|
||||
)
|
||||
<CreateModal
|
||||
getChunkList={getChunkList}
|
||||
isShowCreateModal={isShowCreateModal}
|
||||
chunk_id={chunk_id}
|
||||
doc_id={doc_id}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ kSearchModel, chunkModel, loading }) => ({ kSearchModel, chunkModel, loading }))(Index);
|
||||
export default KnowledgeSearching;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Effect, Reducer, Subscription } from 'umi'
|
||||
import { message } from 'antd';
|
||||
import kbService from '@/services/kbService';
|
||||
import omit from 'lodash/omit';
|
||||
import { DvaModel } from 'umi';
|
||||
|
||||
export interface kSearchModelState {
|
||||
export interface KSearchModelState {
|
||||
loading: boolean;
|
||||
data: any[];
|
||||
total: number;
|
||||
@ -13,26 +13,10 @@ export interface kSearchModelState {
|
||||
question: string;
|
||||
doc_ids: any[];
|
||||
pagination: any;
|
||||
doc_id: string
|
||||
doc_id: string;
|
||||
}
|
||||
|
||||
}
|
||||
export interface chunkgModelType {
|
||||
namespace: 'kSearchModel';
|
||||
state: kSearchModelState;
|
||||
effects: {
|
||||
chunk_list: Effect;
|
||||
get_chunk: Effect;
|
||||
create_hunk: Effect;
|
||||
switch_chunk: Effect;
|
||||
rm_chunk: Effect;
|
||||
getKfList: Effect;
|
||||
};
|
||||
reducers: {
|
||||
updateState: Reducer<kSearchModelState>;
|
||||
};
|
||||
subscriptions: { setup: Subscription };
|
||||
}
|
||||
const Model: chunkgModelType = {
|
||||
const model: DvaModel<KSearchModelState> = {
|
||||
namespace: 'kSearchModel',
|
||||
state: {
|
||||
loading: false,
|
||||
@ -45,114 +29,132 @@ const Model: chunkgModelType = {
|
||||
question: '',
|
||||
doc_ids: [],
|
||||
pagination: { page: 1, size: 30 },
|
||||
doc_id: ''
|
||||
doc_id: '',
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen(location => {
|
||||
console.log(location)
|
||||
history.listen((location) => {
|
||||
console.log(location);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
*getKfList({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.get_document_list, payload);
|
||||
const { data, response } = yield call(
|
||||
kbService.get_document_list,
|
||||
payload,
|
||||
);
|
||||
|
||||
const { retcode, data: res, retmsg } = data
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
d_list: res
|
||||
}
|
||||
d_list: res,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
* chunk_list({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.retrieval_test, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
*chunk_list({ payload = {} }, { call, put, select }) {
|
||||
const { question, doc_ids, pagination }: KSearchModelState = yield select(
|
||||
(state: any) => state.kSearchModel,
|
||||
);
|
||||
const { data } = yield call(kbService.retrieval_test, {
|
||||
...payload,
|
||||
...pagination,
|
||||
question,
|
||||
doc_ids,
|
||||
similarity_threshold: 0.1,
|
||||
});
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
console.log(res)
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
data: res.chunks,
|
||||
total: res.total,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
});
|
||||
callback && callback()
|
||||
|
||||
}
|
||||
},
|
||||
*switch_chunk({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.switch_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
*switch_chunk({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(
|
||||
kbService.switch_chunk,
|
||||
omit(payload, ['kb_id']),
|
||||
);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
callback && callback()
|
||||
|
||||
yield put({
|
||||
type: 'chunk_list',
|
||||
payload: {
|
||||
kb_id: payload.kb_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
*rm_chunk({ payload = {}, callback }, { call, put }) {
|
||||
console.log('shanchu')
|
||||
const { data, response } = yield call(kbService.rm_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
*rm_chunk({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.rm_chunk, {
|
||||
chunk_ids: payload.chunk_ids,
|
||||
});
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
callback && callback()
|
||||
|
||||
// TODO: Can be extracted
|
||||
yield put({
|
||||
type: 'chunk_list',
|
||||
payload: {
|
||||
kb_id: payload.kb_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
* get_chunk({ payload = {}, callback }, { call, put }) {
|
||||
*get_chunk({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.get_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
chunkInfo: res
|
||||
}
|
||||
chunkInfo: res,
|
||||
},
|
||||
});
|
||||
callback && callback(res)
|
||||
|
||||
}
|
||||
},
|
||||
*create_hunk({ payload = {} }, { call, put }) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: true
|
||||
}
|
||||
loading: true,
|
||||
},
|
||||
});
|
||||
let service = kbService.create_chunk
|
||||
let service = kbService.create_chunk;
|
||||
if (payload.chunk_id) {
|
||||
service = kbService.set_chunk
|
||||
service = kbService.set_chunk;
|
||||
}
|
||||
const { data, response } = yield call(service, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: false
|
||||
}
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
isShowCreateModal: false
|
||||
}
|
||||
isShowCreateModal: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
export default Model;
|
||||
export default model;
|
||||
|
||||
@ -1,170 +1,157 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, connect, Dispatch } from 'umi'
|
||||
import { Button, Form, Input, Radio, Select, Tag, Space, } from 'antd';
|
||||
import type { kSModelState } from './model'
|
||||
import type { settingModelState } from '@/pages/setting/model'
|
||||
import styles from './index.less'
|
||||
import { Button, Form, Input, Radio, Select, Space, Tag } from 'antd';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch, useNavigate, useSelector } from 'umi';
|
||||
import styles from './index.less';
|
||||
const { CheckableTag } = Tag;
|
||||
const layout = {
|
||||
labelCol: { span: 8 },
|
||||
wrapperCol: { span: 16 },
|
||||
labelAlign: 'left' as const
|
||||
labelCol: { span: 8 },
|
||||
wrapperCol: { span: 16 },
|
||||
labelAlign: 'left' as const,
|
||||
};
|
||||
const { Option } = Select
|
||||
const { Option } = Select;
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
|
||||
interface kSProps {
|
||||
dispatch: Dispatch;
|
||||
kSModel: kSModelState;
|
||||
settingModel: settingModelState;
|
||||
kb_id: string
|
||||
kb_id: string;
|
||||
}
|
||||
const Index: React.FC<kSProps> = ({ settingModel, kSModel, dispatch, kb_id }) => {
|
||||
let navigate = useNavigate();
|
||||
const { tenantIfo = {} } = settingModel
|
||||
const { parser_ids = '', embd_id = '' } = tenantIfo
|
||||
const [form] = Form.useForm();
|
||||
const KnowledgeSetting: React.FC<kSProps> = ({ kb_id }) => {
|
||||
const dispatch = useDispatch();
|
||||
const settingModel = useSelector((state: any) => state.settingModel);
|
||||
let navigate = useNavigate();
|
||||
const { tenantIfo = {} } = settingModel;
|
||||
const { parser_ids = '', embd_id = '' } = tenantIfo;
|
||||
const [form] = Form.useForm();
|
||||
const [selectedTag, setSelectedTag] = useState('');
|
||||
const values = Form.useWatch([], form);
|
||||
|
||||
useEffect(() => {
|
||||
const getTenantInfo = useCallback(async () => {
|
||||
dispatch({
|
||||
type: 'settingModel/getTenantInfo',
|
||||
payload: {},
|
||||
});
|
||||
if (kb_id) {
|
||||
const data = await dispatch<any>({
|
||||
type: 'kSModel/getKbDetail',
|
||||
payload: {
|
||||
kb_id,
|
||||
},
|
||||
});
|
||||
if (data.retcode === 0) {
|
||||
const { description, name, permission, embd_id } = data.data;
|
||||
form.setFieldsValue({ description, name, permission, embd_id });
|
||||
setSelectedTag(data.data.parser_id);
|
||||
}
|
||||
}
|
||||
}, [kb_id]);
|
||||
|
||||
const onFinish = async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
|
||||
if (kb_id) {
|
||||
dispatch({
|
||||
type: 'settingModel/getTenantInfo',
|
||||
payload: {
|
||||
}
|
||||
type: 'kSModel/updateKb',
|
||||
payload: {
|
||||
...values,
|
||||
parser_id: selectedTag,
|
||||
kb_id,
|
||||
embd_id: undefined,
|
||||
},
|
||||
});
|
||||
if (kb_id) {
|
||||
} else {
|
||||
const retcode = await dispatch<any>({
|
||||
type: 'kSModel/createKb',
|
||||
payload: {
|
||||
...values,
|
||||
parser_id: selectedTag,
|
||||
},
|
||||
});
|
||||
retcode === 0 &&
|
||||
navigate(`/knowledge/add/setting?activeKey=file&id=${kb_id}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: 'kSModel/getKbDetail',
|
||||
payload: {
|
||||
kb_id
|
||||
},
|
||||
callback(detail: any) {
|
||||
console.log(detail)
|
||||
const { description, name, permission, embd_id } = detail
|
||||
form.setFieldsValue({ description, name, permission, embd_id })
|
||||
setSelectedTag(detail.parser_id)
|
||||
}
|
||||
});
|
||||
}
|
||||
useEffect(() => {
|
||||
getTenantInfo();
|
||||
}, [getTenantInfo]);
|
||||
|
||||
}, [kb_id])
|
||||
const [selectedTag, setSelectedTag] = useState('')
|
||||
const values = Form.useWatch([], form);
|
||||
console.log(values, '......变化')
|
||||
const onFinish = () => {
|
||||
form.validateFields().then(
|
||||
() => {
|
||||
if (kb_id) {
|
||||
dispatch({
|
||||
type: 'kSModel/updateKb',
|
||||
payload: {
|
||||
...values,
|
||||
parser_id: selectedTag,
|
||||
kb_id,
|
||||
embd_id: undefined
|
||||
}
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'kSModel/createKb',
|
||||
payload: {
|
||||
...values,
|
||||
parser_id: selectedTag
|
||||
},
|
||||
callback(id: string) {
|
||||
navigate(`/knowledge/add/setting?activeKey=file&id=${kb_id}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
() => {
|
||||
const handleChange = (tag: string, checked: boolean) => {
|
||||
const nextSelectedTag = checked ? tag : selectedTag;
|
||||
console.log('You are interested in: ', nextSelectedTag);
|
||||
setSelectedTag(nextSelectedTag);
|
||||
};
|
||||
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
const handleChange = (tag: string, checked: boolean) => {
|
||||
const nextSelectedTag = checked
|
||||
? tag
|
||||
: selectedTag;
|
||||
console.log('You are interested in: ', nextSelectedTag);
|
||||
setSelectedTag(nextSelectedTag);
|
||||
};
|
||||
|
||||
return <Form
|
||||
{...layout}
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
style={{ maxWidth: 1000, padding: 14 }}
|
||||
return (
|
||||
<Form
|
||||
{...layout}
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
style={{ maxWidth: 1000, padding: 14 }}
|
||||
>
|
||||
<Form.Item name='name' label="知识库名称" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='description' label="知识库描述">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<Form.Item name="permission" label="可见权限">
|
||||
<Radio.Group>
|
||||
<Radio value="me">只有我</Radio>
|
||||
<Radio value="team">所有团队成员</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="embd_id"
|
||||
label="Embedding 模型"
|
||||
hasFeedback
|
||||
rules={[{ required: true, message: 'Please select your country!' }]}
|
||||
>
|
||||
<Select placeholder="Please select a country" >
|
||||
{embd_id.split(',').map((item: string) => {
|
||||
return <Option value={item} key={item}>{item}</Option>
|
||||
})}
|
||||
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<div style={{ marginTop: '5px' }}>
|
||||
修改Embedding 模型,请去<span style={{ color: '#1677ff' }}>设置</span>
|
||||
<Form.Item name="name" label="知识库名称" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="知识库描述">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<Form.Item name="permission" label="可见权限">
|
||||
<Radio.Group>
|
||||
<Radio value="me">只有我</Radio>
|
||||
<Radio value="team">所有团队成员</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="embd_id"
|
||||
label="Embedding 模型"
|
||||
hasFeedback
|
||||
rules={[{ required: true, message: 'Please select your country!' }]}
|
||||
>
|
||||
<Select placeholder="Please select a country">
|
||||
{embd_id.split(',').map((item: string) => {
|
||||
return (
|
||||
<Option value={item} key={item}>
|
||||
{item}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<div style={{ marginTop: '5px' }}>
|
||||
修改Embedding 模型,请去<span style={{ color: '#1677ff' }}>设置</span>
|
||||
</div>
|
||||
<Space size={[0, 8]} wrap>
|
||||
<div className={styles.tags}>
|
||||
{parser_ids.split(',').map((tag: string) => {
|
||||
return (
|
||||
<CheckableTag
|
||||
key={tag}
|
||||
checked={selectedTag === tag}
|
||||
onChange={(checked) => handleChange(tag, checked)}
|
||||
>
|
||||
{tag}
|
||||
</CheckableTag>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Space size={[0, 8]} wrap>
|
||||
<div className={styles.tags}>
|
||||
{
|
||||
parser_ids.split(',').map((tag: string) => {
|
||||
return (<CheckableTag
|
||||
key={tag}
|
||||
checked={selectedTag === tag}
|
||||
onChange={(checked) => handleChange(tag, checked)}
|
||||
>
|
||||
{tag}
|
||||
</CheckableTag>)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</Space>
|
||||
<Space size={[0, 8]} wrap>
|
||||
|
||||
</Space>
|
||||
<div className={styles.preset}>
|
||||
<div className={styles.left}>
|
||||
xxxxx文章
|
||||
</div>
|
||||
<div className={styles.right}>
|
||||
预估份数
|
||||
</div>
|
||||
</div>
|
||||
<Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 8 }}>
|
||||
<Button type="primary" onClick={onFinish}>
|
||||
保存并处理
|
||||
</Button>
|
||||
<Button htmlType="button" style={{ marginLeft: '20px' }}>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Space>
|
||||
<Space size={[0, 8]} wrap></Space>
|
||||
<div className={styles.preset}>
|
||||
<div className={styles.left}>xxxxx文章</div>
|
||||
<div className={styles.right}>预估份数</div>
|
||||
</div>
|
||||
<Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 8 }}>
|
||||
<Button type="primary" onClick={onFinish}>
|
||||
保存并处理
|
||||
</Button>
|
||||
<Button htmlType="button" style={{ marginLeft: '20px' }}>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default connect(({ settingModel, kSModel, loading }) => ({ settingModel, kSModel, loading }))(Index);
|
||||
export default KnowledgeSetting;
|
||||
|
||||
@ -1,72 +1,54 @@
|
||||
import { message } from 'antd';
|
||||
import { Effect, Reducer, Subscription } from 'umi'
|
||||
import kbService from '@/services/kbService';
|
||||
import { message } from 'antd';
|
||||
import { DvaModel } from 'umi';
|
||||
|
||||
export interface kSModelState {
|
||||
export interface KSModelState {
|
||||
isShowPSwModal: boolean;
|
||||
isShowTntModal: boolean;
|
||||
loading: boolean;
|
||||
tenantIfo: any
|
||||
tenantIfo: any;
|
||||
}
|
||||
export interface kSModelType {
|
||||
namespace: 'kSModel';
|
||||
state: kSModelState;
|
||||
effects: {
|
||||
createKb: Effect;
|
||||
updateKb: Effect;
|
||||
getKbDetail: Effect;
|
||||
};
|
||||
reducers: {
|
||||
updateState: Reducer<kSModelState>;
|
||||
};
|
||||
subscriptions: { setup: Subscription };
|
||||
}
|
||||
const Model: kSModelType = {
|
||||
|
||||
const model: DvaModel<KSModelState> = {
|
||||
namespace: 'kSModel',
|
||||
state: {
|
||||
isShowPSwModal: false,
|
||||
isShowTntModal: false,
|
||||
loading: false,
|
||||
tenantIfo: {}
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen(location => {
|
||||
});
|
||||
}
|
||||
},
|
||||
effects: {
|
||||
* createKb({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.createKb, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
if (retcode === 0) {
|
||||
message.success('创建知识库成功!');
|
||||
callback && callback(res.kb_id)
|
||||
}
|
||||
},
|
||||
* updateKb({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.updateKb, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
if (retcode === 0) {
|
||||
message.success('更新知识库成功!');
|
||||
}
|
||||
},
|
||||
*getKbDetail({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.get_kb_detail, payload);
|
||||
const { retcode, data: res, retmsg } = data
|
||||
if (retcode === 0) {
|
||||
// localStorage.setItem('userInfo',res.)
|
||||
callback && callback(res)
|
||||
}
|
||||
},
|
||||
tenantIfo: {},
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload
|
||||
...payload,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen((location) => {});
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
*createKb({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.createKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('创建知识库成功!');
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
*updateKb({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.updateKb, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('更新知识库成功!');
|
||||
}
|
||||
},
|
||||
*getKbDetail({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.get_kb_detail, payload);
|
||||
|
||||
return data;
|
||||
},
|
||||
},
|
||||
};
|
||||
export default Model;
|
||||
export default model;
|
||||
|
||||
Reference in New Issue
Block a user