* feat: use DvaModel instead of redundant types such as kAModelType * feat: set the type for registerServer * feat: remove loading from modeltags/v0.1.0
| import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil'; | |||||
| import { useSelector } from 'umi'; | |||||
| // Get the loading status of given effects under a certain namespace | |||||
| export const useOneNamespaceEffectsLoading = ( | |||||
| namespace: string, | |||||
| effectNames: Array<string>, | |||||
| ) => { | |||||
| const effects = useSelector((state: any) => state.loading.effects); | |||||
| return getOneNamespaceEffectsLoading(namespace, effects, effectNames); | |||||
| }; |
| 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 = { | type FieldType = { | ||||
| content_ltks?: string; | |||||
| content_ltks?: string; | |||||
| }; | }; | ||||
| interface kFProps { | 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; |
| import React, { useEffect, useRef, useState } from 'react'; | |||||
| import { PlusOutlined } from '@ant-design/icons'; | |||||
| import type { InputRef } from 'antd'; | 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(); | |||||
| } | |||||
| }, [inputVisible]); | |||||
| useEffect(() => { | |||||
| if (inputVisible) { | |||||
| inputRef.current?.focus(); | |||||
| } | |||||
| }, [inputVisible]); | |||||
| useEffect(() => { | |||||
| editInputRef.current?.focus(); | |||||
| }, [editInputValue]); | |||||
| useEffect(() => { | |||||
| editInputRef.current?.focus(); | |||||
| }, [editInputValue]); | |||||
| const handleClose = (removedTag: string) => { | |||||
| const newTags = tags.filter((tag) => tag !== removedTag); | |||||
| console.log(newTags); | |||||
| setTags(newTags); | |||||
| }; | |||||
| const handleClose = (removedTag: string) => { | |||||
| const newTags = tags.filter((tag) => tag !== removedTag); | |||||
| console.log(newTags); | |||||
| setTags(newTags); | |||||
| }; | |||||
| const showInput = () => { | |||||
| setInputVisible(true); | |||||
| }; | |||||
| const showInput = () => { | |||||
| setInputVisible(true); | |||||
| }; | |||||
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||||
| setInputValue(e.target.value); | |||||
| }; | |||||
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||||
| setInputValue(e.target.value); | |||||
| }; | |||||
| const handleInputConfirm = () => { | |||||
| if (inputValue && !tags.includes(inputValue)) { | |||||
| setTags([...tags, inputValue]); | |||||
| } | |||||
| setInputVisible(false); | |||||
| setInputValue(''); | |||||
| }; | |||||
| const handleInputConfirm = () => { | |||||
| if (inputValue && !tags.includes(inputValue)) { | |||||
| setTags([...tags, inputValue]); | |||||
| } | |||||
| setInputVisible(false); | |||||
| setInputValue(''); | |||||
| }; | |||||
| const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||||
| setEditInputValue(e.target.value); | |||||
| }; | |||||
| const handleEditInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||||
| setEditInputValue(e.target.value); | |||||
| }; | |||||
| const handleEditInputConfirm = () => { | |||||
| const newTags = [...tags]; | |||||
| newTags[editInputIndex] = editInputValue; | |||||
| setTags(newTags); | |||||
| setEditInputIndex(-1); | |||||
| setEditInputValue(''); | |||||
| }; | |||||
| 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 tagInputStyle: React.CSSProperties = { | |||||
| width: 64, | |||||
| height: 22, | |||||
| marginInlineEnd: 8, | |||||
| verticalAlign: 'top', | |||||
| }; | |||||
| const tagPlusStyle: React.CSSProperties = { | |||||
| height: 22, | |||||
| background: token.colorBgContainer, | |||||
| borderStyle: 'dashed', | |||||
| }; | |||||
| 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} | |||||
| /> | |||||
| ); | |||||
| 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; |
| 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 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 { 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; | |||||
| } | |||||
| interface IProps { | |||||
| doc_id: string; | |||||
| } | } | ||||
| 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 }) | |||||
| 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 [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) => { | 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, | doc_id, | ||||
| keywords: value || keywords, | keywords: value || keywords, | ||||
| available_int | |||||
| } | |||||
| available_int, | |||||
| }; | |||||
| if (payload.available_int === -1) { | if (payload.available_int === -1) { | ||||
| delete payload.available_int | |||||
| delete payload.available_int; | |||||
| } | } | ||||
| dispatch({ | dispatch({ | ||||
| type: 'chunkModel/chunk_list', | type: 'chunkModel/chunk_list', | ||||
| payload: { | payload: { | ||||
| ...payload, | ...payload, | ||||
| ...pagination | |||||
| } | |||||
| ...pagination, | |||||
| }, | |||||
| }); | }); | ||||
| } | |||||
| const confirm = (id: string) => { | |||||
| console.log(id) | |||||
| dispatch({ | |||||
| }; | |||||
| const confirm = async (id: string) => { | |||||
| const retcode = await dispatch<any>({ | |||||
| type: 'chunkModel/rm_chunk', | type: 'chunkModel/rm_chunk', | ||||
| payload: { | payload: { | ||||
| chunk_ids: [id] | |||||
| chunk_ids: [id], | |||||
| }, | }, | ||||
| callback: getChunkList | |||||
| }); | }); | ||||
| retcode === 0 && getChunkList(); | |||||
| }; | }; | ||||
| const handleEditchunk = (chunk_id?: string) => { | const handleEditchunk = (chunk_id?: string) => { | ||||
| dispatch({ | dispatch({ | ||||
| type: 'chunkModel/updateState', | type: 'chunkModel/updateState', | ||||
| payload: { | payload: { | ||||
| isShowCreateModal: true, | isShowCreateModal: true, | ||||
| chunk_id, | 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', | type: 'chunkModel/switch_chunk', | ||||
| payload: { | payload: { | ||||
| chunk_ids: [id], | chunk_ids: [id], | ||||
| available_int: Number(available_int), | available_int: Number(available_int), | ||||
| doc_id | |||||
| doc_id, | |||||
| }, | }, | ||||
| callback: getChunkList | |||||
| }); | }); | ||||
| } | |||||
| retcode === 0 && getChunkList(); | |||||
| }; | |||||
| useEffect(() => { | 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) | |||||
| } | |||||
| 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: '未启用', | |||||
| }, | |||||
| ]} | |||||
| /> | |||||
| 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); | |||||
| }; | |||||
| 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> | </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> | |||||
| </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> | |||||
| <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> | ||||
| </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} | |||||
| /> | |||||
| </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 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; |
| import kbService from '@/services/kbService'; | import kbService from '@/services/kbService'; | ||||
| import { Effect, Reducer } from 'umi'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface chunkModelState { | |||||
| loading: boolean; | |||||
| export interface ChunkModelState { | |||||
| data: any[]; | data: any[]; | ||||
| total: number; | total: number; | ||||
| isShowCreateModal: boolean; | isShowCreateModal: boolean; | ||||
| doc_id: string; | doc_id: string; | ||||
| chunkInfo: any; | 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', | namespace: 'chunkModel', | ||||
| state: { | state: { | ||||
| loading: false, | |||||
| data: [], | data: [], | ||||
| total: 0, | total: 0, | ||||
| isShowCreateModal: false, | isShowCreateModal: false, | ||||
| doc_id: '', | doc_id: '', | ||||
| chunkInfo: {}, | chunkInfo: {}, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| // subscriptions: { | // subscriptions: { | ||||
| // setup({ dispatch, history }) { | // setup({ dispatch, history }) { | ||||
| // history.listen(location => { | // history.listen(location => { | ||||
| // } | // } | ||||
| // }, | // }, | ||||
| effects: { | effects: { | ||||
| *chunk_list({ payload = {}, callback }, { call, put }) { | |||||
| *chunk_list({ payload = {} }, { call, put }) { | |||||
| const { data, response } = yield call(kbService.chunk_list, payload); | const { data, response } = yield call(kbService.chunk_list, payload); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| payload: { | payload: { | ||||
| data: res.chunks, | data: res.chunks, | ||||
| total: res.total, | 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 { data, response } = yield call(kbService.switch_chunk, payload); | ||||
| const { retcode, data: res, retmsg } = data; | 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'); | console.log('shanchu'); | ||||
| const { data, response } = yield call(kbService.rm_chunk, payload); | const { data, response } = yield call(kbService.rm_chunk, payload); | ||||
| const { retcode, data: res, retmsg } = data; | 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 { data, response } = yield call(kbService.get_chunk, payload); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| chunkInfo: res, | chunkInfo: res, | ||||
| }, | }, | ||||
| }); | }); | ||||
| callback && callback(res); | |||||
| } | } | ||||
| return data; | |||||
| }, | }, | ||||
| *create_hunk({ payload = {} }, { call, put }) { | *create_hunk({ payload = {} }, { call, put }) { | ||||
| yield put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| loading: true, | |||||
| }, | |||||
| }); | |||||
| let service = kbService.create_chunk; | let service = kbService.create_chunk; | ||||
| if (payload.chunk_id) { | if (payload.chunk_id) { | ||||
| service = kbService.set_chunk; | service = kbService.set_chunk; | ||||
| } | } | ||||
| const { data, response } = yield call(service, payload); | 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, | |||||
| }, | |||||
| }); | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| } | } | ||||
| }, | }, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| 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 = { | type FieldType = { | ||||
| name?: string; | |||||
| name?: string; | |||||
| }; | }; | ||||
| interface kFProps { | 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; |
| 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 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 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 { | 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 getKfList = (keywords?: string) => { | |||||
| const payload = { | |||||
| kb_id, | |||||
| keywords, | |||||
| }; | |||||
| if (!keywords) { | |||||
| delete payload.keywords; | |||||
| } | } | ||||
| 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() | |||||
| } | |||||
| }); | |||||
| dispatch({ | |||||
| type: 'kFModel/getKfList', | |||||
| payload, | |||||
| }); | |||||
| }; | |||||
| useEffect(() => { | |||||
| if (kb_id) { | |||||
| getKfList(); | |||||
| } | } | ||||
| const showCEFModal = () => { | |||||
| dispatch({ | |||||
| type: 'kFModel/updateState', | |||||
| payload: { | |||||
| isShowCEFwModal: true | |||||
| } | |||||
| }); | |||||
| }; | |||||
| }, [kb_id]); | |||||
| 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> | |||||
| 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, | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| ), | |||||
| }, | |||||
| { | |||||
| 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}`); | |||||
| } | |||||
| 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> | |||||
| ), | |||||
| }, | |||||
| 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> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| 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} /> | |||||
| </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; |
| import kbService from '@/services/kbService'; | import kbService from '@/services/kbService'; | ||||
| import { message } from 'antd'; | 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; | isShowCEFwModal: boolean; | ||||
| isShowTntModal: boolean; | isShowTntModal: boolean; | ||||
| isShowSegmentSetModal: boolean; | isShowSegmentSetModal: boolean; | ||||
| loading: boolean; | |||||
| tenantIfo: any; | tenantIfo: any; | ||||
| data: 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', | namespace: 'kFModel', | ||||
| state: { | state: { | ||||
| isShowCEFwModal: false, | isShowCEFwModal: false, | ||||
| isShowTntModal: false, | isShowTntModal: false, | ||||
| isShowSegmentSetModal: false, | isShowSegmentSetModal: false, | ||||
| loading: false, | |||||
| tenantIfo: {}, | tenantIfo: {}, | ||||
| data: [], | data: [], | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| subscriptions: { | subscriptions: { | ||||
| setup({ dispatch, history }) { | setup({ dispatch, history }) { | ||||
| history.listen((location) => {}); | history.listen((location) => {}); | ||||
| }, | }, | ||||
| }, | }, | ||||
| effects: { | effects: { | ||||
| *createKf({ payload = {}, callback }, { call, put }) { | |||||
| *createKf({ payload = {} }, { call, put }) { | |||||
| const { data, response } = yield call(kbService.createKb, payload); | const { data, response } = yield call(kbService.createKb, payload); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| message.success('创建成功!'); | message.success('创建成功!'); | ||||
| } | } | ||||
| }, | }, | ||||
| *updateKf({ payload = {}, callback }, { call, put }) { | |||||
| *updateKf({ payload = {} }, { call, put }) { | |||||
| const { data, response } = yield call(kbService.updateKb, payload); | const { data, response } = yield call(kbService.updateKb, payload); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| } | } | ||||
| }, | }, | ||||
| *getKfList({ payload = {} }, { call, put }) { | *getKfList({ payload = {} }, { call, put }) { | ||||
| yield put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| loading: true, | |||||
| }, | |||||
| }); | |||||
| const { data, response } = yield call( | const { data, response } = yield call( | ||||
| kbService.get_document_list, | kbService.get_document_list, | ||||
| payload, | payload, | ||||
| ); | ); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| yield put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| loading: false, | |||||
| }, | |||||
| }); | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| }); | }); | ||||
| } | } | ||||
| }, | }, | ||||
| *updateDocumentStatus({ payload = {}, callback }, { call, put }) { | |||||
| yield put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| loading: true, | |||||
| }, | |||||
| }); | |||||
| *updateDocumentStatus({ payload = {} }, { call, put }) { | |||||
| const { data, response } = yield call( | const { data, response } = yield call( | ||||
| kbService.document_change_status, | kbService.document_change_status, | ||||
| payload, | |||||
| pick(payload, ['doc_id', 'status']), | |||||
| ); | ); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| message.success('修改成功!'); | 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; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| message.success('删除成功!'); | 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 { data, response } = yield call(kbService.document_create, payload); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| put({ | |||||
| type: 'kFModel/updateState', | |||||
| payload: { | |||||
| isShowCEFwModal: false, | |||||
| }, | |||||
| }); | |||||
| message.success('创建成功!'); | message.success('创建成功!'); | ||||
| callback && callback(); | |||||
| } | } | ||||
| return retcode; | |||||
| }, | }, | ||||
| *document_change_parser({ payload = {}, callback }, { call, put }) { | |||||
| *document_change_parser({ payload = {} }, { call, put }) { | |||||
| const { data, response } = yield call( | const { data, response } = yield call( | ||||
| kbService.document_change_parser, | kbService.document_change_parser, | ||||
| payload, | payload, | ||||
| ); | ); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| isShowSegmentSetModal: false, | |||||
| }, | |||||
| }); | |||||
| message.success('修改成功!'); | message.success('修改成功!'); | ||||
| callback && callback(); | |||||
| } | } | ||||
| }, | |||||
| }, | |||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| return retcode; | |||||
| }, | }, | ||||
| }, | }, | ||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| 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 styles from './index.less'; | ||||
| import type { kFModelState } from './model' | |||||
| import type { settingModelState } from '@/pages/setting/model' | |||||
| const { CheckableTag } = Tag; | const { CheckableTag } = Tag; | ||||
| interface kFProps { | 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; |
| import React from 'react'; | |||||
| import { connect } from 'umi' | |||||
| import uploadService from '@/services/uploadService'; | |||||
| import type { UploadProps } from 'antd'; | import type { UploadProps } from 'antd'; | ||||
| import { Button, Upload } from 'antd'; | import { Button, Upload } from 'antd'; | ||||
| import uploadService from '@/services/uploadService' | |||||
| import React from 'react'; | |||||
| interface PropsType { | interface PropsType { | ||||
| kb_id: string; | |||||
| getKfList: () => void | |||||
| kb_id: string; | |||||
| getKfList: () => void; | |||||
| } | } | ||||
| type UploadRequestOption = Parameters< | type UploadRequestOption = Parameters< | ||||
| NonNullable<UploadProps["customRequest"]> | |||||
| NonNullable<UploadProps['customRequest']> | |||||
| >[0]; | >[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; |
| 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 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 { debounce } from 'lodash'; | ||||
| import type { kSearchModelState } from './model' | |||||
| import type { chunkModelState } from '../knowledge-chunk/model' | |||||
| import styles from './index.less'; | |||||
| interface chunkProps { | 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 = () => { | 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({ | dispatch({ | ||||
| type: 'kSearchModel/chunk_list', | type: 'kSearchModel/chunk_list', | ||||
| payload: { | payload: { | ||||
| ...payload, | |||||
| ...pagination | |||||
| } | |||||
| kb_id, | |||||
| }, | |||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| const confirm = (id: string) => { | const confirm = (id: string) => { | ||||
| console.log(id) | |||||
| dispatch({ | dispatch({ | ||||
| type: 'kSearchModel/rm_chunk', | type: 'kSearchModel/rm_chunk', | ||||
| payload: { | payload: { | ||||
| chunk_ids: [id] | |||||
| chunk_ids: [id], | |||||
| kb_id, | |||||
| }, | }, | ||||
| callback: getChunkList | |||||
| }); | }); | ||||
| }; | }; | ||||
| const handleEditchunk = (item: any) => { | const handleEditchunk = (item: any) => { | ||||
| const { chunk_id, doc_id } = item | |||||
| const { chunk_id, doc_id } = item; | |||||
| dispatch({ | dispatch({ | ||||
| type: 'chunkModel/updateState', | type: 'chunkModel/updateState', | ||||
| payload: { | payload: { | ||||
| isShowCreateModal: true, | isShowCreateModal: true, | ||||
| chunk_id, | chunk_id, | ||||
| doc_id | |||||
| doc_id, | |||||
| }, | }, | ||||
| callback: getChunkList | |||||
| }); | }); | ||||
| } | |||||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = (page, size) => { | |||||
| getChunkList(); | |||||
| }; | |||||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = ( | |||||
| page, | |||||
| size, | |||||
| ) => { | |||||
| dispatch({ | dispatch({ | ||||
| type: 'kSearchModel/updateState', | type: 'kSearchModel/updateState', | ||||
| payload: { | payload: { | ||||
| pagination: { page, size } | |||||
| } | |||||
| pagination: { page, size }, | |||||
| }, | |||||
| }); | }); | ||||
| }; | }; | ||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch({ | dispatch({ | ||||
| type: 'kSearchModel/updateState', | type: 'kSearchModel/updateState', | ||||
| payload: { | payload: { | ||||
| loading: false, | |||||
| doc_ids: [], | doc_ids: [], | ||||
| question: "" | |||||
| } | |||||
| question: '', | |||||
| }, | |||||
| }); | }); | ||||
| dispatch({ | dispatch({ | ||||
| type: 'kSearchModel/getKfList', | type: 'kSearchModel/getKfList', | ||||
| payload: { | payload: { | ||||
| kb_id | |||||
| } | |||||
| kb_id, | |||||
| }, | |||||
| }); | }); | ||||
| }, []) | |||||
| }, []); | |||||
| const switchChunk = (item: any, available_int: boolean) => { | 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({ | dispatch({ | ||||
| type: 'kSearchModel/switch_chunk', | type: 'kSearchModel/switch_chunk', | ||||
| payload: { | payload: { | ||||
| chunk_ids: [chunk_id], | chunk_ids: [chunk_id], | ||||
| doc_id, | doc_id, | ||||
| available_int | |||||
| available_int, | |||||
| kb_id, | |||||
| }, | }, | ||||
| callback: getChunkList | |||||
| }); | }); | ||||
| } | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| getChunkList() | |||||
| }, [doc_ids, pagination, question]) | |||||
| getChunkList(); | |||||
| }, [doc_ids, pagination, question]); | |||||
| const debounceChange = debounce((value) => { | const debounceChange = debounce((value) => { | ||||
| dispatch({ | dispatch({ | ||||
| type: 'kSearchModel/updateState', | type: 'kSearchModel/updateState', | ||||
| payload: { | 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({ | dispatch({ | ||||
| type: 'kSearchModel/updateState', | type: 'kSearchModel/updateState', | ||||
| payload: { | 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) }} | |||||
| <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} | |||||
| > | > | ||||
| <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> | |||||
| <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> | </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} | |||||
| /> | |||||
| </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> | </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; |
| import { Effect, Reducer, Subscription } from 'umi' | |||||
| import { message } from 'antd'; | |||||
| import kbService from '@/services/kbService'; | import kbService from '@/services/kbService'; | ||||
| import omit from 'lodash/omit'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface kSearchModelState { | |||||
| export interface KSearchModelState { | |||||
| loading: boolean; | loading: boolean; | ||||
| data: any[]; | data: any[]; | ||||
| total: number; | total: number; | ||||
| question: string; | question: string; | ||||
| doc_ids: any[]; | doc_ids: any[]; | ||||
| pagination: any; | pagination: any; | ||||
| 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 }; | |||||
| doc_id: string; | |||||
| } | } | ||||
| const Model: chunkgModelType = { | |||||
| const model: DvaModel<KSearchModelState> = { | |||||
| namespace: 'kSearchModel', | namespace: 'kSearchModel', | ||||
| state: { | state: { | ||||
| loading: false, | loading: false, | ||||
| question: '', | question: '', | ||||
| doc_ids: [], | doc_ids: [], | ||||
| pagination: { page: 1, size: 30 }, | pagination: { page: 1, size: 30 }, | ||||
| doc_id: '' | |||||
| doc_id: '', | |||||
| }, | |||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | }, | ||||
| subscriptions: { | subscriptions: { | ||||
| setup({ dispatch, history }) { | setup({ dispatch, history }) { | ||||
| history.listen(location => { | |||||
| console.log(location) | |||||
| history.listen((location) => { | |||||
| console.log(location); | |||||
| }); | }); | ||||
| } | |||||
| }, | |||||
| }, | }, | ||||
| effects: { | effects: { | ||||
| *getKfList({ payload = {} }, { call, put }) { | *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) { | if (retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| payload: { | 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) { | if (retcode === 0) { | ||||
| console.log(res) | |||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| payload: { | payload: { | ||||
| data: res.chunks, | data: res.chunks, | ||||
| total: res.total, | 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) { | 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) { | 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 { data, response } = yield call(kbService.get_chunk, payload); | ||||
| const { retcode, data: res, retmsg } = data | |||||
| const { retcode, data: res, retmsg } = data; | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| payload: { | payload: { | ||||
| chunkInfo: res | |||||
| } | |||||
| chunkInfo: res, | |||||
| }, | |||||
| }); | }); | ||||
| callback && callback(res) | |||||
| } | } | ||||
| }, | }, | ||||
| *create_hunk({ payload = {} }, { call, put }) { | *create_hunk({ payload = {} }, { call, put }) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| payload: { | payload: { | ||||
| loading: true | |||||
| } | |||||
| loading: true, | |||||
| }, | |||||
| }); | }); | ||||
| let service = kbService.create_chunk | |||||
| let service = kbService.create_chunk; | |||||
| if (payload.chunk_id) { | if (payload.chunk_id) { | ||||
| service = kbService.set_chunk | |||||
| service = kbService.set_chunk; | |||||
| } | } | ||||
| const { data, response } = yield call(service, payload); | const { data, response } = yield call(service, payload); | ||||
| const { retcode, data: res, retmsg } = data | |||||
| const { retcode, data: res, retmsg } = data; | |||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| payload: { | payload: { | ||||
| loading: false | |||||
| } | |||||
| loading: false, | |||||
| }, | |||||
| }); | }); | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| payload: { | payload: { | ||||
| isShowCreateModal: false | |||||
| } | |||||
| isShowCreateModal: false, | |||||
| }, | |||||
| }); | }); | ||||
| } | } | ||||
| }, | }, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload | |||||
| }; | |||||
| } | |||||
| } | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| 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 { CheckableTag } = Tag; | ||||
| const layout = { | 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 */ | /* eslint-disable no-template-curly-in-string */ | ||||
| interface kSProps { | 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(() => { | |||||
| dispatch({ | |||||
| type: 'settingModel/getTenantInfo', | |||||
| payload: { | |||||
| } | |||||
| }); | |||||
| if (kb_id) { | |||||
| 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) | |||||
| } | |||||
| }); | |||||
| } | |||||
| }, [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 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: 'kSModel/updateKb', | |||||
| payload: { | |||||
| ...values, | |||||
| parser_id: selectedTag, | |||||
| kb_id, | |||||
| embd_id: undefined, | |||||
| }, | |||||
| }); | |||||
| } 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); | |||||
| } | |||||
| }; | |||||
| }; | |||||
| useEffect(() => { | |||||
| getTenantInfo(); | |||||
| }, [getTenantInfo]); | |||||
| 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> | |||||
| </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> | |||||
| <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> | </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> | </Form> | ||||
| } | |||||
| ); | |||||
| }; | |||||
| export default connect(({ settingModel, kSModel, loading }) => ({ settingModel, kSModel, loading }))(Index); | |||||
| export default KnowledgeSetting; |
| import { message } from 'antd'; | |||||
| import { Effect, Reducer, Subscription } from 'umi' | |||||
| import kbService from '@/services/kbService'; | import kbService from '@/services/kbService'; | ||||
| import { message } from 'antd'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface kSModelState { | |||||
| export interface KSModelState { | |||||
| isShowPSwModal: boolean; | isShowPSwModal: boolean; | ||||
| isShowTntModal: boolean; | isShowTntModal: boolean; | ||||
| loading: boolean; | |||||
| tenantIfo: any | |||||
| } | |||||
| export interface kSModelType { | |||||
| namespace: 'kSModel'; | |||||
| state: kSModelState; | |||||
| effects: { | |||||
| createKb: Effect; | |||||
| updateKb: Effect; | |||||
| getKbDetail: Effect; | |||||
| }; | |||||
| reducers: { | |||||
| updateState: Reducer<kSModelState>; | |||||
| }; | |||||
| subscriptions: { setup: Subscription }; | |||||
| tenantIfo: any; | |||||
| } | } | ||||
| const Model: kSModelType = { | |||||
| const model: DvaModel<KSModelState> = { | |||||
| namespace: 'kSModel', | namespace: 'kSModel', | ||||
| state: { | state: { | ||||
| isShowPSwModal: false, | isShowPSwModal: false, | ||||
| isShowTntModal: false, | isShowTntModal: false, | ||||
| loading: false, | |||||
| tenantIfo: {} | |||||
| tenantIfo: {}, | |||||
| }, | |||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | }, | ||||
| subscriptions: { | subscriptions: { | ||||
| setup({ dispatch, history }) { | setup({ dispatch, history }) { | ||||
| history.listen(location => { | |||||
| }); | |||||
| } | |||||
| history.listen((location) => {}); | |||||
| }, | |||||
| }, | }, | ||||
| effects: { | effects: { | ||||
| * createKb({ payload = {}, callback }, { call, put }) { | |||||
| const { data, response } = yield call(kbService.createKb, payload); | |||||
| const { retcode, data: res, retmsg } = data | |||||
| *createKb({ payload = {} }, { call, put }) { | |||||
| const { data } = yield call(kbService.createKb, payload); | |||||
| const { retcode } = data; | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| message.success('创建知识库成功!'); | message.success('创建知识库成功!'); | ||||
| callback && callback(res.kb_id) | |||||
| } | } | ||||
| return retcode; | |||||
| }, | }, | ||||
| * updateKb({ payload = {}, callback }, { call, put }) { | |||||
| const { data, response } = yield call(kbService.updateKb, payload); | |||||
| const { retcode, data: res, retmsg } = data | |||||
| *updateKb({ payload = {} }, { call, put }) { | |||||
| const { data } = yield call(kbService.updateKb, payload); | |||||
| const { retcode, data: res, retmsg } = data; | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| message.success('更新知识库成功!'); | 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) | |||||
| } | |||||
| *getKbDetail({ payload = {} }, { call, put }) { | |||||
| const { data } = yield call(kbService.get_kb_detail, payload); | |||||
| return data; | |||||
| }, | }, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload | |||||
| }; | |||||
| } | |||||
| } | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| import { connect, useNavigate, useLocation, Dispatch } from 'umi' | |||||
| import React, { useState, useEffect, useMemo } from 'react'; | |||||
| import { getWidth } from '@/utils'; | |||||
| import { BarsOutlined, SearchOutlined, ToolOutlined } from '@ant-design/icons'; | |||||
| import type { MenuProps } from 'antd'; | import type { MenuProps } from 'antd'; | ||||
| import { Menu } from 'antd'; | import { Menu } from 'antd'; | ||||
| import { | |||||
| ToolOutlined, | |||||
| BarsOutlined, | |||||
| SearchOutlined | |||||
| } from '@ant-design/icons'; | |||||
| import File from './components/knowledge-file' | |||||
| import Setting from './components/knowledge-setting' | |||||
| import Search from './components/knowledge-search' | |||||
| import Chunk from './components/knowledge-chunk' | |||||
| import styles from './index.less' | |||||
| import { getWidth } from '@/utils' | |||||
| import { kAModelState } from './model' | |||||
| import React, { useEffect, useMemo, useState } from 'react'; | |||||
| import { useDispatch, useLocation, useNavigate, 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 styles from './index.less'; | |||||
| const KnowledgeAdding = () => { | |||||
| const dispatch = useDispatch(); | |||||
| const kAModel = useSelector((state: any) => state.kAModel); | |||||
| const { id, activeKey, doc_id } = kAModel; | |||||
| interface kAProps { | |||||
| dispatch: Dispatch; | |||||
| kAModel: kAModelState; | |||||
| } | |||||
| const Index: React.FC<kAProps> = ({ kAModel, dispatch }) => { | |||||
| const [collapsed, setCollapsed] = useState(false); | |||||
| const { id, activeKey, doc_id } = kAModel | |||||
| const [windowWidth, setWindowWidth] = useState(getWidth()); | |||||
| let navigate = useNavigate(); | |||||
| const location = useLocation(); | |||||
| // 标记一下 | |||||
| console.log(doc_id, '>>>>>>>>>>>>>doc_id') | |||||
| useEffect(() => { | |||||
| const widthSize = () => { | |||||
| const width = getWidth() | |||||
| console.log(width) | |||||
| const [collapsed, setCollapsed] = useState(false); | |||||
| const [windowWidth, setWindowWidth] = useState(getWidth()); | |||||
| let navigate = useNavigate(); | |||||
| const location = useLocation(); | |||||
| setWindowWidth(width); | |||||
| }; | |||||
| window.addEventListener("resize", widthSize); | |||||
| return () => { | |||||
| window.removeEventListener("resize", widthSize); | |||||
| }; | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| console.log(location) | |||||
| const search = location.search.slice(1) | |||||
| const map = search.split('&').reduce((obj, cur) => { | |||||
| const [key, value] = cur.split('=') | |||||
| obj[key] = value | |||||
| return obj | |||||
| }, {}) | |||||
| dispatch({ | |||||
| type: 'kAModel/updateState', | |||||
| payload: { | |||||
| doc_id: undefined, | |||||
| ...map, | |||||
| // 标记一下 | |||||
| console.log(doc_id, '>>>>>>>>>>>>>doc_id'); | |||||
| useEffect(() => { | |||||
| const widthSize = () => { | |||||
| const width = getWidth(); | |||||
| console.log(width); | |||||
| } | |||||
| }); | |||||
| }, [location]) | |||||
| useEffect(() => { | |||||
| if (windowWidth.width > 957) { | |||||
| setCollapsed(false) | |||||
| } else { | |||||
| setCollapsed(true) | |||||
| } | |||||
| }, [windowWidth.width]) | |||||
| type MenuItem = Required<MenuProps>['items'][number]; | |||||
| setWindowWidth(width); | |||||
| }; | |||||
| window.addEventListener('resize', widthSize); | |||||
| return () => { | |||||
| window.removeEventListener('resize', widthSize); | |||||
| }; | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| const search: string = location.search.slice(1); | |||||
| const map = search.split('&').reduce<Record<string, string>>((obj, cur) => { | |||||
| const [key, value] = cur.split('='); | |||||
| obj[key] = value; | |||||
| return obj; | |||||
| }, {}); | |||||
| function getItem( | |||||
| label: React.ReactNode, | |||||
| key: React.Key, | |||||
| icon?: React.ReactNode, | |||||
| disabled?: boolean, | |||||
| children?: MenuItem[], | |||||
| type?: 'group', | |||||
| dispatch({ | |||||
| type: 'kAModel/updateState', | |||||
| payload: { | |||||
| doc_id: undefined, | |||||
| ...map, | |||||
| }, | |||||
| }); | |||||
| }, [location]); | |||||
| ): MenuItem { | |||||
| return { | |||||
| key, | |||||
| icon, | |||||
| children, | |||||
| label, | |||||
| type, | |||||
| disabled | |||||
| } as MenuItem; | |||||
| } | |||||
| const items: MenuItem[] = useMemo(() => { | |||||
| const disabled = !id | |||||
| return [ | |||||
| getItem('配置', 'setting', <ToolOutlined />), | |||||
| getItem('知识库', 'file', <BarsOutlined />, disabled), | |||||
| getItem('搜索测试', 'search', <SearchOutlined />, disabled), | |||||
| ] | |||||
| }, [id]); | |||||
| const handleSelect: MenuProps['onSelect'] = (e) => { | |||||
| navigate(`/knowledge/add/setting?activeKey=${e.key}&id=${id}`); | |||||
| useEffect(() => { | |||||
| if (windowWidth.width > 957) { | |||||
| setCollapsed(false); | |||||
| } else { | |||||
| setCollapsed(true); | |||||
| } | } | ||||
| return ( | |||||
| <> | |||||
| <div className={styles.container}> | |||||
| <div className={styles.menu}> | |||||
| <Menu | |||||
| selectedKeys={[activeKey]} | |||||
| mode="inline" | |||||
| className={windowWidth.width > 957 ? styles.defaultWidth : styles.minWidth} | |||||
| inlineCollapsed={collapsed} | |||||
| items={items} | |||||
| onSelect={handleSelect} | |||||
| /> | |||||
| </div> | |||||
| <div className={styles.content}> | |||||
| {activeKey === 'file' && !doc_id && <File kb_id={id} />} | |||||
| {activeKey === 'setting' && <Setting kb_id={id} />} | |||||
| {activeKey === 'search' && <Search kb_id={id} />} | |||||
| {activeKey === 'file' && !!doc_id && <Chunk doc_id={doc_id} />} | |||||
| }, [windowWidth.width]); | |||||
| type MenuItem = Required<MenuProps>['items'][number]; | |||||
| </div> | |||||
| </div> | |||||
| </> | |||||
| ); | |||||
| function getItem( | |||||
| label: React.ReactNode, | |||||
| key: React.Key, | |||||
| icon?: React.ReactNode, | |||||
| disabled?: boolean, | |||||
| children?: MenuItem[], | |||||
| type?: 'group', | |||||
| ): MenuItem { | |||||
| return { | |||||
| key, | |||||
| icon, | |||||
| children, | |||||
| label, | |||||
| type, | |||||
| disabled, | |||||
| } as MenuItem; | |||||
| } | |||||
| const items: MenuItem[] = useMemo(() => { | |||||
| const disabled = !id; | |||||
| return [ | |||||
| getItem('配置', 'setting', <ToolOutlined />), | |||||
| getItem('知识库', 'file', <BarsOutlined />, disabled), | |||||
| getItem('搜索测试', 'search', <SearchOutlined />, disabled), | |||||
| ]; | |||||
| }, [id]); | |||||
| const handleSelect: MenuProps['onSelect'] = (e) => { | |||||
| navigate(`/knowledge/add/setting?activeKey=${e.key}&id=${id}`); | |||||
| }; | |||||
| return ( | |||||
| <> | |||||
| <div className={styles.container}> | |||||
| <div className={styles.menu}> | |||||
| <Menu | |||||
| selectedKeys={[activeKey]} | |||||
| mode="inline" | |||||
| className={ | |||||
| windowWidth.width > 957 ? styles.defaultWidth : styles.minWidth | |||||
| } | |||||
| inlineCollapsed={collapsed} | |||||
| items={items} | |||||
| onSelect={handleSelect} | |||||
| /> | |||||
| </div> | |||||
| <div className={styles.content}> | |||||
| {activeKey === 'file' && !doc_id && <File kb_id={id} />} | |||||
| {activeKey === 'setting' && <Setting kb_id={id} />} | |||||
| {activeKey === 'search' && <Search kb_id={id} />} | |||||
| {activeKey === 'file' && !!doc_id && <Chunk doc_id={doc_id} />} | |||||
| </div> | |||||
| </div> | |||||
| </> | |||||
| ); | |||||
| }; | }; | ||||
| export default connect(({ kAModel, loading }) => ({ kAModel, loading }))(Index); | |||||
| export default KnowledgeAdding; |
| import { Effect, Reducer, Subscription } from 'umi' | |||||
| import { message } from 'antd'; | |||||
| import kbService from '@/services/kbService'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface kAModelState { | export interface kAModelState { | ||||
| isShowPSwModal: boolean; | isShowPSwModal: boolean; | ||||
| isShowTntModal: boolean; | isShowTntModal: boolean; | ||||
| tenantIfo: any; | tenantIfo: any; | ||||
| activeKey: string; | activeKey: string; | ||||
| id: string; | id: string; | ||||
| doc_id: string | |||||
| doc_id: string; | |||||
| } | } | ||||
| export interface kAModelType { | |||||
| namespace: 'kAModel'; | |||||
| state: kAModelState; | |||||
| effects: { | |||||
| }; | |||||
| reducers: { | |||||
| updateState: Reducer<kAModelState>; | |||||
| }; | |||||
| subscriptions: { setup: Subscription }; | |||||
| } | |||||
| const Model: kAModelType = { | |||||
| const model: DvaModel<kAModelState> = { | |||||
| namespace: 'kAModel', | namespace: 'kAModel', | ||||
| state: { | state: { | ||||
| isShowPSwModal: false, | isShowPSwModal: false, | ||||
| tenantIfo: {}, | tenantIfo: {}, | ||||
| activeKey: 'setting', | activeKey: 'setting', | ||||
| id: '', | id: '', | ||||
| doc_id: '' | |||||
| }, | |||||
| subscriptions: { | |||||
| setup({ dispatch, history }) { | |||||
| history.listen(location => { | |||||
| }); | |||||
| } | |||||
| }, | |||||
| effects: { | |||||
| doc_id: '', | |||||
| }, | }, | ||||
| reducers: { | reducers: { | ||||
| updateState(state, { payload }) { | updateState(state, { payload }) { | ||||
| return { | return { | ||||
| ...state, | ...state, | ||||
| ...payload | |||||
| ...payload, | |||||
| }; | }; | ||||
| } | |||||
| } | |||||
| }, | |||||
| }, | |||||
| subscriptions: { | |||||
| setup({ dispatch, history }) { | |||||
| history.listen((location) => {}); | |||||
| }, | |||||
| }, | |||||
| effects: {}, | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| import React from 'react'; | |||||
| import { connect, Dispatch } from 'umi'; | |||||
| import type { chatModelState } from './model' | |||||
| import { useSelector } from 'umi'; | |||||
| interface chatProps { | |||||
| chatModel: chatModelState; | |||||
| dispatch: Dispatch | |||||
| } | |||||
| const View: React.FC<chatProps> = ({ chatModel, dispatch }) => { | |||||
| const { name } = chatModel; | |||||
| return <div>chat:{name} </div>; | |||||
| const Chat = () => { | |||||
| const { name } = useSelector((state: any) => state.chatModel); | |||||
| return <div>chat:{name} </div>; | |||||
| }; | }; | ||||
| export default connect(({ chatModel, loading }) => ({ chatModel, loading }))(View); | |||||
| export default Chat; |
| import { Effect, Reducer, Subscription } from 'umi'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface chatModelState { | |||||
| name: string; | |||||
| export interface ChatModelState { | |||||
| name: string; | |||||
| } | } | ||||
| export interface chatModelType { | |||||
| namespace: 'chatModel'; | |||||
| state: chatModelState; | |||||
| effects: { | |||||
| query: Effect; | |||||
| }; | |||||
| reducers: { | |||||
| save: Reducer<chatModelState>; | |||||
| }; | |||||
| subscriptions: { setup: Subscription }; | |||||
| } | |||||
| const Model: chatModelType = { | |||||
| namespace: 'chatModel', | |||||
| state: { | |||||
| name: 'kate', | |||||
| }, | |||||
| effects: { | |||||
| *query({ payload }, { call, put }) { }, | |||||
| }, | |||||
| reducers: { | |||||
| save(state, action) { | |||||
| return { | |||||
| ...state, | |||||
| ...action.payload, | |||||
| }; | |||||
| }, | |||||
| const model: DvaModel<ChatModelState> = { | |||||
| namespace: 'chatModel', | |||||
| state: { | |||||
| name: 'kate', | |||||
| }, | |||||
| reducers: { | |||||
| save(state, action) { | |||||
| return { | |||||
| ...state, | |||||
| ...action.payload, | |||||
| }; | |||||
| }, | }, | ||||
| subscriptions: { | |||||
| setup({ dispatch, history }) { | |||||
| return history.listen((query) => { | |||||
| console.log(query) | |||||
| }); | |||||
| }, | |||||
| }, | |||||
| subscriptions: { | |||||
| setup({ dispatch, history }) { | |||||
| return history.listen((query) => { | |||||
| console.log(query); | |||||
| }); | |||||
| }, | }, | ||||
| }, | |||||
| effects: { | |||||
| *query({ payload }, { call, put }) {}, | |||||
| }, | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| import React, { useEffect, useState } from 'react'; | |||||
| import { UploadOutlined } from '@ant-design/icons'; | import { UploadOutlined } from '@ant-design/icons'; | ||||
| import { Button, Upload } from 'antd'; | import { Button, Upload } from 'antd'; | ||||
| import type { UploadFile } from 'antd/es/upload/interface'; | |||||
| const App: React.FC = () => { | |||||
| const [fileList, setFileList] = useState([{ | |||||
| uid: '0', | |||||
| name: 'xxx.png', | |||||
| status: 'uploading', | |||||
| percent: 10, | |||||
| }]) | |||||
| const obj = { | |||||
| uid: '-1', | |||||
| name: 'yyy.png', | |||||
| status: 'done', | |||||
| url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', | |||||
| thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', | |||||
| } | |||||
| useEffect(() => { | |||||
| const timer = setInterval(() => { | |||||
| setFileList((fileList: any) => { | |||||
| const percent = fileList[0]?.percent | |||||
| if (percent + 10 >= 100) { | |||||
| clearInterval(timer) | |||||
| return [obj] | |||||
| } | |||||
| const list = [{ ...fileList[0], percent: percent + 10 }] | |||||
| console.log(list) | |||||
| return list | |||||
| }) | |||||
| }, 300) | |||||
| }, []) | |||||
| return ( | |||||
| import React, { useEffect, useState } from 'react'; | |||||
| <> | |||||
| <Upload | |||||
| action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188" | |||||
| listType="picture" | |||||
| fileList={[...fileList]} | |||||
| multiple | |||||
| > | |||||
| <Button icon={<UploadOutlined />}>Upload</Button> | |||||
| </Upload> | |||||
| </> | |||||
| ) | |||||
| const File: React.FC = () => { | |||||
| const [fileList, setFileList] = useState([ | |||||
| { | |||||
| uid: '0', | |||||
| name: 'xxx.png', | |||||
| status: 'uploading', | |||||
| percent: 10, | |||||
| }, | |||||
| ]); | |||||
| const obj = { | |||||
| uid: '-1', | |||||
| name: 'yyy.png', | |||||
| status: 'done', | |||||
| url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', | |||||
| thumbUrl: | |||||
| 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', | |||||
| }; | |||||
| useEffect(() => { | |||||
| const timer = setInterval(() => { | |||||
| setFileList((fileList: any) => { | |||||
| const percent = fileList[0]?.percent; | |||||
| if (percent + 10 >= 100) { | |||||
| clearInterval(timer); | |||||
| return [obj]; | |||||
| } | |||||
| const list = [{ ...fileList[0], percent: percent + 10 }]; | |||||
| console.log(list); | |||||
| return list; | |||||
| }); | |||||
| }, 300); | |||||
| }, []); | |||||
| return ( | |||||
| <> | |||||
| <Upload | |||||
| action="https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188" | |||||
| listType="picture" | |||||
| fileList={[...fileList]} | |||||
| multiple | |||||
| > | |||||
| <Button icon={<UploadOutlined />}>Upload</Button> | |||||
| </Upload> | |||||
| </> | |||||
| ); | |||||
| }; | }; | ||||
| export default App; | |||||
| export default File; |
| PlusOutlined, | PlusOutlined, | ||||
| } from '@ant-design/icons'; | } from '@ant-design/icons'; | ||||
| import { Card, Col, FloatButton, Popconfirm, Row } from 'antd'; | import { Card, Col, FloatButton, Popconfirm, Row } from 'antd'; | ||||
| import React, { useEffect } from 'react'; | |||||
| import { Dispatch, connect, useNavigate } from 'umi'; | |||||
| import { useCallback, useEffect } from 'react'; | |||||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import type { knowledgeModelState } from './model'; | |||||
| interface KnowledgeProps { | |||||
| dispatch: Dispatch; | |||||
| knowledgeModel: knowledgeModelState; | |||||
| } | |||||
| const Index: React.FC<KnowledgeProps> = ({ knowledgeModel, dispatch }) => { | |||||
| const Knowledge = () => { | |||||
| const dispatch = useDispatch(); | |||||
| const knowledgeModel = useSelector((state: any) => state.knowledgeModel); | |||||
| const navigate = useNavigate(); | const navigate = useNavigate(); | ||||
| // const [datas, setDatas] = useState(data) | |||||
| const { data = [] } = knowledgeModel; | const { data = [] } = knowledgeModel; | ||||
| console.log(knowledgeModel); | |||||
| // const x = useSelector((state) => state.knowledgeModel); | |||||
| const fetchList = useCallback(() => { | |||||
| dispatch({ | |||||
| type: 'knowledgeModel/getList', | |||||
| payload: {}, | |||||
| }); | |||||
| }, []); | |||||
| const confirm = (id: string) => { | const confirm = (id: string) => { | ||||
| dispatch({ | dispatch({ | ||||
| payload: { | payload: { | ||||
| kb_id: id, | kb_id: id, | ||||
| }, | }, | ||||
| callback: () => { | |||||
| dispatch({ | |||||
| type: 'knowledgeModel/getList', | |||||
| payload: {}, | |||||
| }); | |||||
| }, | |||||
| }); | }); | ||||
| }; | }; | ||||
| const handleAddKnowledge = () => { | const handleAddKnowledge = () => { | ||||
| navigate(`add/setting?activeKey=file&id=${id}`); | navigate(`add/setting?activeKey=file&id=${id}`); | ||||
| }; | }; | ||||
| useEffect(() => { | useEffect(() => { | ||||
| dispatch({ | |||||
| type: 'knowledgeModel/getList', | |||||
| payload: {}, | |||||
| }); | |||||
| }, []); | |||||
| fetchList(); | |||||
| }, [fetchList]); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <div className={styles.knowledge}> | <div className={styles.knowledge}> | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default connect(({ knowledgeModel, loading }) => ({ | |||||
| knowledgeModel, | |||||
| loading, | |||||
| }))(Index); | |||||
| export default Knowledge; |
| import kbService from '@/services/kbService'; | import kbService from '@/services/kbService'; | ||||
| import { Effect, Reducer } from 'umi'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface knowledgeModelState { | |||||
| loading: boolean; | |||||
| export interface KnowledgeModelState { | |||||
| data: any[]; | data: any[]; | ||||
| } | } | ||||
| export interface knowledgegModelType { | |||||
| namespace: 'knowledgeModel'; | |||||
| state: knowledgeModelState; | |||||
| effects: { | |||||
| rmKb: Effect; | |||||
| getList: Effect; | |||||
| }; | |||||
| reducers: { | |||||
| updateState: Reducer<knowledgeModelState>; | |||||
| }; | |||||
| // subscriptions: { setup: Subscription }; | |||||
| } | |||||
| const Model: knowledgegModelType = { | |||||
| const model: DvaModel<KnowledgeModelState> = { | |||||
| namespace: 'knowledgeModel', | namespace: 'knowledgeModel', | ||||
| state: { | state: { | ||||
| loading: false, | |||||
| data: [], | data: [], | ||||
| }, | }, | ||||
| // subscriptions: { | |||||
| // setup({ dispatch, history }) { | |||||
| // history.listen((location) => { | |||||
| // console.log(location); | |||||
| // }); | |||||
| // }, | |||||
| // }, | |||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| effects: { | effects: { | ||||
| *rmKb({ payload = {}, callback }, { call, put }) { | *rmKb({ payload = {}, callback }, { call, put }) { | ||||
| const { data, response } = yield call(kbService.rmKb, payload); | |||||
| const { retcode, data: res, retmsg } = data; | |||||
| const { data } = yield call(kbService.rmKb, payload); | |||||
| const { retcode } = data; | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| callback && callback(); | |||||
| yield put({ | |||||
| type: 'getList', | |||||
| payload: {}, | |||||
| }); | |||||
| } | } | ||||
| }, | }, | ||||
| *getList({ payload = {} }, { call, put }) { | *getList({ payload = {} }, { call, put }) { | ||||
| yield put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| loading: true, | |||||
| }, | |||||
| }); | |||||
| const { data, response } = yield call(kbService.getList, payload); | |||||
| const { data } = yield call(kbService.getList, payload); | |||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| yield put({ | |||||
| type: 'updateState', | |||||
| payload: { | |||||
| loading: false, | |||||
| }, | |||||
| }); | |||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| type: 'updateState', | type: 'updateState', | ||||
| } | } | ||||
| }, | }, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| import { rsaPsw } from '@/utils'; | import { rsaPsw } from '@/utils'; | ||||
| import { Button, Checkbox, Form, Input } from 'antd'; | import { Button, Checkbox, Form, Input } from 'antd'; | ||||
| import { FC, useEffect, useState } from 'react'; | |||||
| import { Dispatch, Icon, connect, useNavigate } from 'umi'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import { Icon, useDispatch, useNavigate, useSelector } from 'umi'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| interface LoginProps { | |||||
| dispatch: Dispatch; | |||||
| } | |||||
| const View: FC<LoginProps> = ({ dispatch }) => { | |||||
| let navigate = useNavigate(); | |||||
| const Login = () => { | |||||
| const [title, setTitle] = useState('login'); | const [title, setTitle] = useState('login'); | ||||
| let navigate = useNavigate(); | |||||
| const dispatch = useDispatch(); | |||||
| const effectsLoading: any = useSelector<any>( // TODO: Type needs to be improved | |||||
| (state) => state.loading.effects, | |||||
| ); | |||||
| const signLoading = | |||||
| effectsLoading['loginModel/login'] || effectsLoading['loginModel/register']; | |||||
| const changeTitle = () => { | const changeTitle = () => { | ||||
| setTitle((title) => (title === 'login' ? 'register' : 'login')); | setTitle((title) => (title === 'login' ? 'register' : 'login')); | ||||
| }; | }; | ||||
| var rsaPassWord = rsaPsw(params.password); | var rsaPassWord = rsaPsw(params.password); | ||||
| if (title === 'login') { | if (title === 'login') { | ||||
| const ret = await dispatch({ | |||||
| const retcode = await dispatch<any>({ | |||||
| type: 'loginModel/login', | type: 'loginModel/login', | ||||
| payload: { | payload: { | ||||
| email: params.email, | email: params.email, | ||||
| password: rsaPassWord, | password: rsaPassWord, | ||||
| }, | }, | ||||
| }); | }); | ||||
| console.info(ret); | |||||
| navigate('/knowledge'); | |||||
| if (retcode === 0) { | |||||
| navigate('/knowledge'); | |||||
| } | |||||
| } else { | } else { | ||||
| dispatch({ | |||||
| // TODO: Type needs to be improved | |||||
| const retcode = await dispatch<any>({ | |||||
| type: 'loginModel/register', | type: 'loginModel/register', | ||||
| payload: { | payload: { | ||||
| nickname: params.nickname, | nickname: params.nickname, | ||||
| email: params.email, | email: params.email, | ||||
| password: rsaPassWord, | password: rsaPassWord, | ||||
| }, | }, | ||||
| callback() { | |||||
| setTitle('login'); | |||||
| }, | |||||
| }); | }); | ||||
| if (retcode === 0) { | |||||
| setTitle('login'); | |||||
| } | |||||
| } | } | ||||
| } catch (errorInfo) { | } catch (errorInfo) { | ||||
| console.log('Failed:', errorInfo); | console.log('Failed:', errorInfo); | ||||
| label="Password" | label="Password" | ||||
| rules={[{ required: true, message: 'Please input value' }]} | rules={[{ required: true, message: 'Please input value' }]} | ||||
| > | > | ||||
| <Input size="large" placeholder="Please input value" /> | |||||
| <Input.Password size="large" placeholder="Please input value" /> | |||||
| </Form.Item> | </Form.Item> | ||||
| {title === 'login' && ( | {title === 'login' && ( | ||||
| <Form.Item name="remember" valuePropName="checked"> | <Form.Item name="remember" valuePropName="checked"> | ||||
| </div> | </div> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| <Button type="primary" block size="large" onClick={onCheck}> | |||||
| <Button | |||||
| type="primary" | |||||
| block | |||||
| size="large" | |||||
| onClick={onCheck} | |||||
| loading={signLoading} | |||||
| > | |||||
| {title === 'login' ? 'Sign in' : 'Continue'} | {title === 'login' ? 'Sign in' : 'Continue'} | ||||
| </Button> | </Button> | ||||
| {title === 'login' && ( | {title === 'login' && ( | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default connect(({ loginModel, loading }) => ({ loginModel, loading }))( | |||||
| View, | |||||
| ); | |||||
| export default Login; |
| import userService from '@/services/userService'; | import userService from '@/services/userService'; | ||||
| import authorizationUtil from '@/utils/authorizationUtil'; | import authorizationUtil from '@/utils/authorizationUtil'; | ||||
| import { message } from 'antd'; | import { message } from 'antd'; | ||||
| import { Effect, Reducer, Subscription } from 'umi'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface loginModelState { | |||||
| export interface LoginModelState { | |||||
| list: any[]; | list: any[]; | ||||
| info: any; | info: any; | ||||
| visible: boolean; | visible: boolean; | ||||
| } | } | ||||
| export interface logingModelType { | |||||
| namespace: 'loginModel'; | |||||
| state: loginModelState; | |||||
| effects: { | |||||
| login: Effect; | |||||
| register: Effect; | |||||
| }; | |||||
| reducers: { | |||||
| updateState: Reducer<loginModelState>; | |||||
| }; | |||||
| subscriptions: { setup: Subscription }; | |||||
| } | |||||
| const Model: logingModelType = { | |||||
| const model: DvaModel<LoginModelState> = { | |||||
| namespace: 'loginModel', | namespace: 'loginModel', | ||||
| state: { | state: { | ||||
| list: [], | list: [], | ||||
| info: {}, | info: {}, | ||||
| visible: false, | visible: false, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| subscriptions: { | subscriptions: { | ||||
| setup({ dispatch, history }) { | setup({ dispatch, history }) { | ||||
| history.listen((location) => {}); | history.listen((location) => {}); | ||||
| userInfo: JSON.stringify(userInfo), | userInfo: JSON.stringify(userInfo), | ||||
| Token: token, | Token: token, | ||||
| }); | }); | ||||
| // setTimeout(() => { | |||||
| // window.location.href = '/file'; | |||||
| // }, 300); | |||||
| } | } | ||||
| return data; | |||||
| return retcode; | |||||
| }, | }, | ||||
| *register({ payload = {}, callback }, { call, put }) { | |||||
| *register({ payload = {} }, { call, put }) { | |||||
| const { data, response } = yield call(userService.register, payload); | const { data, response } = yield call(userService.register, payload); | ||||
| console.log(); | console.log(); | ||||
| const { retcode, data: res, retmsg } = data; | const { retcode, data: res, retmsg } = data; | ||||
| if (retcode === 0) { | if (retcode === 0) { | ||||
| message.success('注册成功!'); | message.success('注册成功!'); | ||||
| callback && callback(); | |||||
| } | } | ||||
| }, | |||||
| }, | |||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| return retcode; | |||||
| }, | }, | ||||
| }, | }, | ||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| import userService from '@/services/userService'; | import userService from '@/services/userService'; | ||||
| import authorizationUtil from '@/utils/authorizationUtil'; | import authorizationUtil from '@/utils/authorizationUtil'; | ||||
| import { message } from 'antd'; | import { message } from 'antd'; | ||||
| import { Effect, Reducer, Subscription } from 'umi'; | |||||
| import { DvaModel } from 'umi'; | |||||
| export interface settingModelState { | |||||
| export interface SettingModelState { | |||||
| isShowPSwModal: boolean; | isShowPSwModal: boolean; | ||||
| isShowTntModal: boolean; | isShowTntModal: boolean; | ||||
| isShowSAKModal: boolean; | isShowSAKModal: boolean; | ||||
| factoriesList: any[]; | factoriesList: any[]; | ||||
| } | } | ||||
| export interface settingModelType { | |||||
| namespace: 'settingModel'; | |||||
| state: settingModelState; | |||||
| effects: { | |||||
| setting: Effect; | |||||
| getUserInfo: Effect; | |||||
| getTenantInfo: Effect; | |||||
| set_tenant_info: Effect; | |||||
| factories_list: Effect; | |||||
| llm_list: Effect; | |||||
| my_llm: Effect; | |||||
| set_api_key: Effect; | |||||
| }; | |||||
| reducers: { | |||||
| updateState: Reducer<settingModelState>; | |||||
| }; | |||||
| subscriptions: { setup: Subscription }; | |||||
| } | |||||
| const Model: settingModelType = { | |||||
| const model: DvaModel<SettingModelState> = { | |||||
| namespace: 'settingModel', | namespace: 'settingModel', | ||||
| state: { | state: { | ||||
| isShowPSwModal: false, | isShowPSwModal: false, | ||||
| myLlm: [], | myLlm: [], | ||||
| factoriesList: [], | factoriesList: [], | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| subscriptions: { | subscriptions: { | ||||
| setup({ dispatch, history }) { | setup({ dispatch, history }) { | ||||
| history.listen((location) => {}); | history.listen((location) => {}); | ||||
| } | } | ||||
| }, | }, | ||||
| }, | }, | ||||
| reducers: { | |||||
| updateState(state, { payload }) { | |||||
| return { | |||||
| ...state, | |||||
| ...payload, | |||||
| }; | |||||
| }, | |||||
| }, | |||||
| }; | }; | ||||
| export default Model; | |||||
| export default model; |
| get_chunk, | get_chunk, | ||||
| switch_chunk, | switch_chunk, | ||||
| rm_chunk, | rm_chunk, | ||||
| retrieval_test } = api; | |||||
| interface kbService { | |||||
| createKb: () => void; | |||||
| updateKb: () => void; | |||||
| rmKb: () => void; | |||||
| get_kb_detail: () => void; | |||||
| getList: () => void; | |||||
| get_document_list: () => void; | |||||
| document_change_status: () => void; | |||||
| document_rm: () => void; | |||||
| document_create: () => void; | |||||
| document_change_parser: () => void; | |||||
| chunk_list: () => void; | |||||
| create_chunk: () => void; | |||||
| set_chunk: () => void; | |||||
| get_chunk: () => void; | |||||
| switch_chunk: () => void; | |||||
| rm_chunk: () => void; | |||||
| retrieval_test: () => void; | |||||
| } | |||||
| const kbService: kbService = registerServer( | |||||
| { | |||||
| // 知识库管理 | |||||
| createKb: { | |||||
| url: create_kb, | |||||
| method: 'post' | |||||
| }, | |||||
| updateKb: { | |||||
| url: update_kb, | |||||
| method: 'post' | |||||
| }, | |||||
| rmKb: { | |||||
| url: rm_kb, | |||||
| method: 'post' | |||||
| }, | |||||
| get_kb_detail: { | |||||
| url: get_kb_detail, | |||||
| method: 'get' | |||||
| }, | |||||
| getList: { | |||||
| url: kb_list, | |||||
| method: 'get' | |||||
| }, | |||||
| // 文件管理 | |||||
| get_document_list: { | |||||
| url: get_document_list, | |||||
| method: 'get' | |||||
| }, | |||||
| document_change_status: { | |||||
| url: document_change_status, | |||||
| method: 'post' | |||||
| }, | |||||
| document_rm: { | |||||
| url: document_rm, | |||||
| method: 'post' | |||||
| }, | |||||
| document_create: { | |||||
| url: document_create, | |||||
| method: 'post' | |||||
| }, | |||||
| document_change_parser: { | |||||
| url: document_change_parser, | |||||
| method: 'post' | |||||
| }, | |||||
| // chunk管理 | |||||
| chunk_list: { | |||||
| url: chunk_list, | |||||
| method: 'post' | |||||
| }, | |||||
| create_chunk: { | |||||
| url: create_chunk, | |||||
| method: 'post' | |||||
| }, | |||||
| set_chunk: { | |||||
| url: set_chunk, | |||||
| method: 'post' | |||||
| }, | |||||
| get_chunk: { | |||||
| url: get_chunk, | |||||
| method: 'get' | |||||
| }, | |||||
| switch_chunk: { | |||||
| url: switch_chunk, | |||||
| method: 'post' | |||||
| }, | |||||
| rm_chunk: { | |||||
| url: rm_chunk, | |||||
| method: 'post' | |||||
| }, | |||||
| retrieval_test: { | |||||
| url: retrieval_test, | |||||
| method: 'post' | |||||
| }, | |||||
| }, | |||||
| request | |||||
| ); | |||||
| retrieval_test, | |||||
| } = api; | |||||
| const methods = { | |||||
| // 知识库管理 | |||||
| createKb: { | |||||
| url: create_kb, | |||||
| method: 'post', | |||||
| }, | |||||
| updateKb: { | |||||
| url: update_kb, | |||||
| method: 'post', | |||||
| }, | |||||
| rmKb: { | |||||
| url: rm_kb, | |||||
| method: 'post', | |||||
| }, | |||||
| get_kb_detail: { | |||||
| url: get_kb_detail, | |||||
| method: 'get', | |||||
| }, | |||||
| getList: { | |||||
| url: kb_list, | |||||
| method: 'get', | |||||
| }, | |||||
| // 文件管理 | |||||
| get_document_list: { | |||||
| url: get_document_list, | |||||
| method: 'get', | |||||
| }, | |||||
| document_change_status: { | |||||
| url: document_change_status, | |||||
| method: 'post', | |||||
| }, | |||||
| document_rm: { | |||||
| url: document_rm, | |||||
| method: 'post', | |||||
| }, | |||||
| document_create: { | |||||
| url: document_create, | |||||
| method: 'post', | |||||
| }, | |||||
| document_change_parser: { | |||||
| url: document_change_parser, | |||||
| method: 'post', | |||||
| }, | |||||
| // chunk管理 | |||||
| chunk_list: { | |||||
| url: chunk_list, | |||||
| method: 'post', | |||||
| }, | |||||
| create_chunk: { | |||||
| url: create_chunk, | |||||
| method: 'post', | |||||
| }, | |||||
| set_chunk: { | |||||
| url: set_chunk, | |||||
| method: 'post', | |||||
| }, | |||||
| get_chunk: { | |||||
| url: get_chunk, | |||||
| method: 'get', | |||||
| }, | |||||
| switch_chunk: { | |||||
| url: switch_chunk, | |||||
| method: 'post', | |||||
| }, | |||||
| rm_chunk: { | |||||
| url: rm_chunk, | |||||
| method: 'post', | |||||
| }, | |||||
| retrieval_test: { | |||||
| url: retrieval_test, | |||||
| method: 'post', | |||||
| }, | |||||
| }; | |||||
| const kbService = registerServer<keyof typeof methods>(methods, request); | |||||
| export default kbService; | export default kbService; |
| import request from '@/utils/request'; | import request from '@/utils/request'; | ||||
| const { | const { | ||||
| login, register, setting, user_info, tenant_info, factories_list, llm_list, my_llm, set_api_key, set_tenant_info } = api; | |||||
| interface userServiceType { | |||||
| login: (params: any) => void | |||||
| } | |||||
| const userService = registerServer( | |||||
| { | |||||
| login: { | |||||
| url: login, | |||||
| method: 'post', | |||||
| login, | |||||
| register, | |||||
| setting, | |||||
| user_info, | |||||
| tenant_info, | |||||
| factories_list, | |||||
| llm_list, | |||||
| my_llm, | |||||
| set_api_key, | |||||
| set_tenant_info, | |||||
| } = api; | |||||
| }, | |||||
| register: { | |||||
| url: register, | |||||
| method: 'post' | |||||
| }, | |||||
| setting: { | |||||
| url: setting, | |||||
| method: 'post' | |||||
| }, | |||||
| user_info: { | |||||
| url: user_info, | |||||
| method: 'get' | |||||
| }, | |||||
| get_tenant_info: { | |||||
| url: tenant_info, | |||||
| method: 'get' | |||||
| }, | |||||
| set_tenant_info: { | |||||
| url: set_tenant_info, | |||||
| method: 'post' | |||||
| }, | |||||
| factories_list: { | |||||
| url: factories_list, | |||||
| method: 'get' | |||||
| }, | |||||
| llm_list: { | |||||
| url: llm_list, | |||||
| method: 'get' | |||||
| }, | |||||
| my_llm: { | |||||
| url: my_llm, | |||||
| method: 'get' | |||||
| }, | |||||
| set_api_key: { | |||||
| url: set_api_key, | |||||
| method: 'post' | |||||
| }, | |||||
| }, | |||||
| request | |||||
| ); | |||||
| const methods = { | |||||
| login: { | |||||
| url: login, | |||||
| method: 'post', | |||||
| }, | |||||
| register: { | |||||
| url: register, | |||||
| method: 'post', | |||||
| }, | |||||
| setting: { | |||||
| url: setting, | |||||
| method: 'post', | |||||
| }, | |||||
| user_info: { | |||||
| url: user_info, | |||||
| method: 'get', | |||||
| }, | |||||
| get_tenant_info: { | |||||
| url: tenant_info, | |||||
| method: 'get', | |||||
| }, | |||||
| set_tenant_info: { | |||||
| url: set_tenant_info, | |||||
| method: 'post', | |||||
| }, | |||||
| factories_list: { | |||||
| url: factories_list, | |||||
| method: 'get', | |||||
| }, | |||||
| llm_list: { | |||||
| url: llm_list, | |||||
| method: 'get', | |||||
| }, | |||||
| my_llm: { | |||||
| url: my_llm, | |||||
| method: 'get', | |||||
| }, | |||||
| set_api_key: { | |||||
| url: set_api_key, | |||||
| method: 'post', | |||||
| }, | |||||
| } as const; | |||||
| const userService = registerServer<keyof typeof methods>(methods, request); | |||||
| export default userService; | export default userService; |
| const registerServer = (opt: any, request: any): any => { | |||||
| let server = {}; | |||||
| import { RequestMethod } from 'umi-request'; | |||||
| type Service<T extends string> = Record<T, (params: any) => any>; | |||||
| const registerServer = <T extends string>( | |||||
| opt: Record<T, { url: string; method: string }>, | |||||
| request: RequestMethod, | |||||
| ) => { | |||||
| const server: Service<T> = {} as Service<T>; | |||||
| for (let key in opt) { | for (let key in opt) { | ||||
| server[key] = (params: any) => { | |||||
| server[key] = (params) => { | |||||
| if (opt[key].method === 'post' || opt[key].method === 'POST') { | if (opt[key].method === 'post' || opt[key].method === 'POST') { | ||||
| return request(opt[key].url, { | return request(opt[key].url, { | ||||
| method: opt[key].method, | method: opt[key].method, | ||||
| data: params | |||||
| data: params, | |||||
| }); | }); | ||||
| } | } | ||||
| if (opt[key].method === 'get' || opt[key].method === 'GET') { | if (opt[key].method === 'get' || opt[key].method === 'GET') { | ||||
| return request.get(opt[key].url, { | return request.get(opt[key].url, { | ||||
| params | |||||
| params, | |||||
| }); | }); | ||||
| } | } | ||||
| }; | }; |
| import { message, notification } from 'antd'; | import { message, notification } from 'antd'; | ||||
| import { extend } from 'umi-request'; | |||||
| import { RequestMethod, extend } from 'umi-request'; | |||||
| import { Authorization } from '@/constants/authorization'; | import { Authorization } from '@/constants/authorization'; | ||||
| import api from '@/utils/api'; | import api from '@/utils/api'; | ||||
| const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message | const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message | ||||
| const retcodeMessage = { | |||||
| const RetcodeMessage = { | |||||
| 200: '服务器成功返回请求的数据。', | 200: '服务器成功返回请求的数据。', | ||||
| 201: '新建或修改数据成功。', | 201: '新建或修改数据成功。', | ||||
| 202: '一个请求已经进入后台排队(异步任务)。', | 202: '一个请求已经进入后台排队(异步任务)。', | ||||
| 503: '服务不可用,服务器暂时过载或维护。', | 503: '服务不可用,服务器暂时过载或维护。', | ||||
| 504: '网关超时。', | 504: '网关超时。', | ||||
| }; | }; | ||||
| type retcode = | |||||
| type ResultCode = | |||||
| | 200 | | 200 | ||||
| | 201 | | 201 | ||||
| | 202 | | 202 | ||||
| /** | /** | ||||
| * 异常处理程序 | * 异常处理程序 | ||||
| */ | */ | ||||
| interface responseType { | |||||
| interface ResponseType { | |||||
| retcode: number; | retcode: number; | ||||
| data: any; | data: any; | ||||
| retmsg: string; | retmsg: string; | ||||
| } else { | } else { | ||||
| if (response && response.status) { | if (response && response.status) { | ||||
| const errorText = | const errorText = | ||||
| retcodeMessage[response.status as retcode] || response.statusText; | |||||
| RetcodeMessage[response.status as ResultCode] || response.statusText; | |||||
| const { status, url } = response; | const { status, url } = response; | ||||
| notification.error({ | notification.error({ | ||||
| message: `请求错误 ${status}: ${url}`, | message: `请求错误 ${status}: ${url}`, | ||||
| /** | /** | ||||
| * 配置request请求时的默认参数 | * 配置request请求时的默认参数 | ||||
| */ | */ | ||||
| const request = extend({ | |||||
| const request: RequestMethod = extend({ | |||||
| errorHandler, // 默认错误处理 | errorHandler, // 默认错误处理 | ||||
| timeout: 3000000, | timeout: 3000000, | ||||
| getResponse: true, | getResponse: true, | ||||
| request.interceptors.response.use(async (response: any, request) => { | request.interceptors.response.use(async (response: any, request) => { | ||||
| console.log(response, request); | console.log(response, request); | ||||
| const data: responseType = await response.clone().json(); | |||||
| const data: ResponseType = await response.clone().json(); | |||||
| // response 拦截 | // response 拦截 | ||||
| if (data.retcode === 401 || data.retcode === 401) { | if (data.retcode === 401 || data.retcode === 401) { |
| export const getOneNamespaceEffectsLoading = ( | |||||
| namespace: string, | |||||
| effects: Record<string, boolean>, | |||||
| effectNames: Array<string>, | |||||
| ) => { | |||||
| return effectNames.some( | |||||
| (effectName) => effects[`${namespace}/${effectName}`], | |||||
| ); | |||||
| }; |