* feat: use DvaModel instead of redundant types such as kAModelType * feat: set the type for registerServer * feat: remove loading from modeltags/v0.1.0
| @@ -0,0 +1,11 @@ | |||
| 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); | |||
| }; | |||
| @@ -1,102 +1,116 @@ | |||
| import React, { useEffect, useState } from 'react' | |||
| import { connect, Dispatch } from 'umi'; | |||
| import i18n from 'i18next'; | |||
| import { useTranslation, Trans } from 'react-i18next' | |||
| import { Input, Modal, Form } from 'antd' | |||
| import styles from './index.less'; | |||
| import type { chunkModelState } from './model' | |||
| import EditTag from './editTag' | |||
| import { Form, Input, Modal } from 'antd'; | |||
| import React, { useCallback, useEffect, useState } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch } from 'umi'; | |||
| import EditTag from './editTag'; | |||
| type FieldType = { | |||
| content_ltks?: string; | |||
| content_ltks?: string; | |||
| }; | |||
| interface kFProps { | |||
| dispatch: Dispatch; | |||
| chunkModel: chunkModelState; | |||
| getChunkList: () => void; | |||
| isShowCreateModal: boolean; | |||
| doc_id: string; | |||
| chunk_id: string | |||
| getChunkList: () => void; | |||
| isShowCreateModal: boolean; | |||
| doc_id: string; | |||
| chunk_id: string; | |||
| } | |||
| const Index: React.FC<kFProps> = ({ dispatch, getChunkList, doc_id, isShowCreateModal, chunk_id }) => { | |||
| // const { , chunkInfo } = chunkModel | |||
| const [important_kwd, setImportantKwd] = useState(['Unremovable', 'Tag 2', 'Tag 3']); | |||
| const { t } = useTranslation() | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| isShowCreateModal: false | |||
| } | |||
| }); | |||
| }; | |||
| useEffect(() => { | |||
| console.log(chunk_id, isShowCreateModal) | |||
| if (chunk_id && isShowCreateModal) { | |||
| dispatch({ | |||
| type: 'chunkModel/get_chunk', | |||
| payload: { | |||
| chunk_id | |||
| }, | |||
| callback(info: any) { | |||
| console.log(info) | |||
| const { content_ltks, important_kwd = [] } = info | |||
| form.setFieldsValue({ content_ltks }) | |||
| setImportantKwd(important_kwd) | |||
| } | |||
| }); | |||
| } | |||
| }, [chunk_id, isShowCreateModal]) | |||
| const [form] = Form.useForm() | |||
| const handleOk = async () => { | |||
| try { | |||
| const values = await form.validateFields(); | |||
| dispatch({ | |||
| type: 'chunkModel/create_hunk', | |||
| payload: { | |||
| content_ltks: values.content_ltks, | |||
| doc_id, | |||
| chunk_id, | |||
| important_kwd | |||
| }, | |||
| callback: () => { | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| isShowCreateModal: false | |||
| } | |||
| }); | |||
| getChunkList && getChunkList() | |||
| } | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }; | |||
| const Index: React.FC<kFProps> = ({ | |||
| getChunkList, | |||
| doc_id, | |||
| isShowCreateModal, | |||
| chunk_id, | |||
| }) => { | |||
| const dispatch = useDispatch(); | |||
| const [form] = Form.useForm(); | |||
| return ( | |||
| <Modal title="Basic Modal" open={isShowCreateModal} onOk={handleOk} onCancel={handleCancel}> | |||
| <Form | |||
| form={form} | |||
| name="validateOnly" | |||
| labelCol={{ span: 5 }} | |||
| wrapperCol={{ span: 19 }} | |||
| style={{ maxWidth: 600 }} | |||
| autoComplete="off" | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="chunk 内容" | |||
| name="content_ltks" | |||
| rules={[{ required: true, message: 'Please input value!' }]} | |||
| > | |||
| <Input.TextArea /> | |||
| </Form.Item> | |||
| <EditTag tags={important_kwd} setTags={setImportantKwd} /> | |||
| </Form> | |||
| </Modal > | |||
| // const { , chunkInfo } = chunkModel | |||
| const [important_kwd, setImportantKwd] = useState([ | |||
| 'Unremovable', | |||
| 'Tag 2', | |||
| 'Tag 3', | |||
| ]); | |||
| const { t } = useTranslation(); | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| isShowCreateModal: false, | |||
| }, | |||
| }); | |||
| }; | |||
| const getChunk = useCallback(async () => { | |||
| if (chunk_id && isShowCreateModal) { | |||
| const data = await dispatch<any>({ | |||
| type: 'chunkModel/get_chunk', | |||
| payload: { | |||
| chunk_id, | |||
| }, | |||
| }); | |||
| ); | |||
| } | |||
| export default connect(({ chunkModel, loading }) => ({ chunkModel, loading }))(Index); | |||
| if (data?.retcode === 0) { | |||
| const { content_ltks, important_kwd = [] } = data.data; | |||
| form.setFieldsValue({ content_ltks }); | |||
| setImportantKwd(important_kwd); | |||
| } | |||
| } | |||
| }, [chunk_id, isShowCreateModal]); | |||
| useEffect(() => { | |||
| getChunk(); | |||
| }, [getChunk]); | |||
| const handleOk = async () => { | |||
| try { | |||
| const values = await form.validateFields(); | |||
| dispatch({ | |||
| type: 'chunkModel/create_hunk', | |||
| payload: { | |||
| content_ltks: values.content_ltks, | |||
| doc_id, | |||
| chunk_id, | |||
| important_kwd, | |||
| }, | |||
| // callback: () => { | |||
| // dispatch({ | |||
| // type: 'chunkModel/updateState', | |||
| // payload: { | |||
| // isShowCreateModal: false, | |||
| // }, | |||
| // }); | |||
| // getChunkList && getChunkList(); | |||
| // }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="Basic Modal" | |||
| open={isShowCreateModal} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| > | |||
| <Form | |||
| form={form} | |||
| name="validateOnly" | |||
| labelCol={{ span: 5 }} | |||
| wrapperCol={{ span: 19 }} | |||
| style={{ maxWidth: 600 }} | |||
| autoComplete="off" | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="chunk 内容" | |||
| name="content_ltks" | |||
| rules={[{ required: true, message: 'Please input value!' }]} | |||
| > | |||
| <Input.TextArea /> | |||
| </Form.Item> | |||
| <EditTag tags={important_kwd} setTags={setImportantKwd} /> | |||
| </Form> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default Index; | |||
| @@ -1,142 +1,141 @@ | |||
| import React, { useEffect, useRef, useState } from 'react'; | |||
| import { PlusOutlined } from '@ant-design/icons'; | |||
| import type { InputRef } from 'antd'; | |||
| import { Input, Space, Tag, theme, Tooltip } from 'antd'; | |||
| interface editTagsProps { | |||
| tags: any[], | |||
| setTags: (tags: any[]) => void | |||
| import { Input, Space, Tag, Tooltip, theme } from 'antd'; | |||
| import React, { useEffect, useRef, useState } from 'react'; | |||
| interface EditTagsProps { | |||
| tags: any[]; | |||
| setTags: (tags: any[]) => void; | |||
| } | |||
| const App: React.FC<editTagsProps> = ({ tags, setTags }) => { | |||
| const { token } = theme.useToken(); | |||
| const EditTag: React.FC<EditTagsProps> = ({ tags, setTags }) => { | |||
| const { token } = theme.useToken(); | |||
| const [inputVisible, setInputVisible] = useState(false); | |||
| const [inputValue, setInputValue] = useState(''); | |||
| const [editInputIndex, setEditInputIndex] = useState(-1); | |||
| const [editInputValue, setEditInputValue] = useState(''); | |||
| const inputRef = useRef<InputRef>(null); | |||
| const editInputRef = useRef<InputRef>(null); | |||
| const [inputVisible, setInputVisible] = useState(false); | |||
| const [inputValue, setInputValue] = useState(''); | |||
| const [editInputIndex, setEditInputIndex] = useState(-1); | |||
| const [editInputValue, setEditInputValue] = useState(''); | |||
| const inputRef = useRef<InputRef>(null); | |||
| const editInputRef = useRef<InputRef>(null); | |||
| useEffect(() => { | |||
| if (inputVisible) { | |||
| inputRef.current?.focus(); | |||
| } | |||
| }, [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; | |||
| @@ -1,225 +1,282 @@ | |||
| import React, { useEffect, useState, useCallback } from 'react'; | |||
| import { useNavigate, connect, Dispatch } from 'umi' | |||
| import { Card, Row, Col, Input, Select, Switch, Pagination, Spin, Button, Popconfirm } from 'antd'; | |||
| import { MinusSquareOutlined, DeleteOutlined, } from '@ant-design/icons'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil'; | |||
| import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons'; | |||
| import type { PaginationProps } from 'antd'; | |||
| import { api_host } from '@/utils/api' | |||
| import CreateModal from './components/createModal' | |||
| import { | |||
| Button, | |||
| Card, | |||
| Col, | |||
| Input, | |||
| Pagination, | |||
| Popconfirm, | |||
| Row, | |||
| Select, | |||
| Spin, | |||
| Switch, | |||
| } from 'antd'; | |||
| import React, { useCallback, useEffect, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| import CreateModal from './components/createModal'; | |||
| import styles from './index.less' | |||
| import { debounce } from 'lodash'; | |||
| import type { chunkModelState } from './model' | |||
| interface chunkProps { | |||
| dispatch: Dispatch; | |||
| chunkModel: chunkModelState; | |||
| doc_id: string | |||
| import styles from './index.less'; | |||
| interface PayloadType { | |||
| doc_id: string; | |||
| keywords?: string; | |||
| available_int?: number; | |||
| } | |||
| 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 { data = [], total, loading, chunk_id, isShowCreateModal } = chunkModel | |||
| console.log(chunkModel) | |||
| const { data = [], total, chunk_id, isShowCreateModal } = chunkModel; | |||
| const effects = useSelector((state: any) => state.loading.effects); | |||
| const loading = getOneNamespaceEffectsLoading('chunkModel', effects, [ | |||
| 'create_hunk', | |||
| 'chunk_list', | |||
| 'switch_chunk', | |||
| ]); | |||
| const getChunkList = (value?: string) => { | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| loading: true | |||
| } | |||
| }); | |||
| interface payloadType { | |||
| doc_id: string; | |||
| keywords?: string; | |||
| available_int?: number | |||
| } | |||
| const payload: payloadType = { | |||
| const payload: PayloadType = { | |||
| doc_id, | |||
| keywords: value || keywords, | |||
| available_int | |||
| } | |||
| available_int, | |||
| }; | |||
| if (payload.available_int === -1) { | |||
| delete payload.available_int | |||
| delete payload.available_int; | |||
| } | |||
| dispatch({ | |||
| type: 'chunkModel/chunk_list', | |||
| payload: { | |||
| ...payload, | |||
| ...pagination | |||
| } | |||
| ...pagination, | |||
| }, | |||
| }); | |||
| } | |||
| const confirm = (id: string) => { | |||
| console.log(id) | |||
| dispatch({ | |||
| }; | |||
| const confirm = async (id: string) => { | |||
| const retcode = await dispatch<any>({ | |||
| type: 'chunkModel/rm_chunk', | |||
| payload: { | |||
| chunk_ids: [id] | |||
| chunk_ids: [id], | |||
| }, | |||
| callback: getChunkList | |||
| }); | |||
| retcode === 0 && getChunkList(); | |||
| }; | |||
| const handleEditchunk = (chunk_id?: string) => { | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| isShowCreateModal: true, | |||
| chunk_id, | |||
| doc_id | |||
| doc_id, | |||
| }, | |||
| callback: getChunkList | |||
| }); | |||
| } | |||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = (page, size) => { | |||
| setPagination({ page, size }) | |||
| getChunkList(); | |||
| }; | |||
| const switchChunk = (id: string, available_int: boolean) => { | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| loading: true | |||
| } | |||
| }); | |||
| dispatch({ | |||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = ( | |||
| page, | |||
| size, | |||
| ) => { | |||
| setPagination({ page, size }); | |||
| }; | |||
| const switchChunk = async (id: string, available_int: boolean) => { | |||
| const retcode = await dispatch<any>({ | |||
| type: 'chunkModel/switch_chunk', | |||
| payload: { | |||
| chunk_ids: [id], | |||
| available_int: Number(available_int), | |||
| doc_id | |||
| doc_id, | |||
| }, | |||
| callback: getChunkList | |||
| }); | |||
| } | |||
| retcode === 0 && getChunkList(); | |||
| }; | |||
| useEffect(() => { | |||
| getChunkList() | |||
| }, [doc_id, available_int, pagination]) | |||
| const debounceChange = debounce(getChunkList, 300) | |||
| const debounceCallback = useCallback((value: string) => debounceChange(value), []) | |||
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |||
| const value = e.target.value | |||
| SetKeywords(value) | |||
| debounceCallback(value) | |||
| } | |||
| 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> | |||
| <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> | |||
| </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 > | |||
| <CreateModal doc_id={doc_id} isShowCreateModal={isShowCreateModal} chunk_id={chunk_id} getChunkList={getChunkList} /> | |||
| </> | |||
| ) | |||
| <CreateModal | |||
| doc_id={doc_id} | |||
| isShowCreateModal={isShowCreateModal} | |||
| chunk_id={chunk_id} | |||
| getChunkList={getChunkList} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| export default connect(({ chunkModel, loading }) => ({ chunkModel, loading }))(Index); | |||
| export default Chunk; | |||
| @@ -1,8 +1,7 @@ | |||
| import kbService from '@/services/kbService'; | |||
| import { Effect, Reducer } from 'umi'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface chunkModelState { | |||
| loading: boolean; | |||
| export interface ChunkModelState { | |||
| data: any[]; | |||
| total: number; | |||
| isShowCreateModal: boolean; | |||
| @@ -10,25 +9,10 @@ export interface chunkModelState { | |||
| doc_id: string; | |||
| chunkInfo: any; | |||
| } | |||
| export interface chunkgModelType { | |||
| namespace: 'chunkModel'; | |||
| state: chunkModelState; | |||
| effects: { | |||
| chunk_list: Effect; | |||
| get_chunk: Effect; | |||
| create_hunk: Effect; | |||
| switch_chunk: Effect; | |||
| rm_chunk: Effect; | |||
| }; | |||
| reducers: { | |||
| updateState: Reducer<chunkModelState>; | |||
| }; | |||
| // subscriptions: { setup: Subscription }; | |||
| } | |||
| const Model: chunkgModelType = { | |||
| const model: DvaModel<ChunkModelState> = { | |||
| namespace: 'chunkModel', | |||
| state: { | |||
| loading: false, | |||
| data: [], | |||
| total: 0, | |||
| isShowCreateModal: false, | |||
| @@ -36,6 +20,14 @@ const Model: chunkgModelType = { | |||
| doc_id: '', | |||
| chunkInfo: {}, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| // subscriptions: { | |||
| // setup({ dispatch, history }) { | |||
| // history.listen(location => { | |||
| @@ -44,7 +36,7 @@ const Model: chunkgModelType = { | |||
| // } | |||
| // }, | |||
| effects: { | |||
| *chunk_list({ payload = {}, callback }, { call, put }) { | |||
| *chunk_list({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.chunk_list, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| @@ -55,28 +47,23 @@ const Model: chunkgModelType = { | |||
| payload: { | |||
| data: res.chunks, | |||
| total: res.total, | |||
| loading: false, | |||
| }, | |||
| }); | |||
| callback && callback(); | |||
| } | |||
| }, | |||
| *switch_chunk({ payload = {}, callback }, { call, put }) { | |||
| *switch_chunk({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.switch_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| callback && callback(); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *rm_chunk({ payload = {}, callback }, { call, put }) { | |||
| *rm_chunk({ payload = {} }, { call, put }) { | |||
| console.log('shanchu'); | |||
| const { data, response } = yield call(kbService.rm_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| callback && callback(); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *get_chunk({ payload = {}, callback }, { call, put }) { | |||
| *get_chunk({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.get_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| @@ -86,28 +73,16 @@ const Model: chunkgModelType = { | |||
| chunkInfo: res, | |||
| }, | |||
| }); | |||
| callback && callback(res); | |||
| } | |||
| return data; | |||
| }, | |||
| *create_hunk({ payload = {} }, { call, put }) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: true, | |||
| }, | |||
| }); | |||
| let service = kbService.create_chunk; | |||
| if (payload.chunk_id) { | |||
| service = kbService.set_chunk; | |||
| } | |||
| const { data, response } = yield call(service, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: false, | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| @@ -118,13 +93,5 @@ const Model: chunkgModelType = { | |||
| } | |||
| }, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -1,79 +1,73 @@ | |||
| import React from 'react' | |||
| import { connect, Dispatch } from 'umi'; | |||
| import i18n from 'i18next'; | |||
| import { useTranslation, Trans } from 'react-i18next' | |||
| import { Input, Modal, Form } from 'antd' | |||
| import styles from './index.less'; | |||
| import type { kFModelState } from './model' | |||
| import { Form, Input, Modal } from 'antd'; | |||
| import React from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| type FieldType = { | |||
| name?: string; | |||
| name?: string; | |||
| }; | |||
| interface kFProps { | |||
| dispatch: Dispatch; | |||
| kFModel: kFModelState; | |||
| getKfList: () => void; | |||
| kb_id: string | |||
| getKfList: () => void; | |||
| kb_id: string; | |||
| } | |||
| const Index: React.FC<kFProps> = ({ kFModel, dispatch, getKfList, kb_id }) => { | |||
| const { isShowCEFwModal } = kFModel | |||
| const { t } = useTranslation() | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: false | |||
| } | |||
| }); | |||
| }; | |||
| const [form] = Form.useForm() | |||
| const handleOk = async () => { | |||
| try { | |||
| const values = await form.validateFields(); | |||
| dispatch({ | |||
| type: 'kFModel/document_create', | |||
| payload: { | |||
| name: values.name, | |||
| kb_id | |||
| }, | |||
| callback: () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: false | |||
| } | |||
| }); | |||
| getKfList && getKfList() | |||
| } | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }; | |||
| const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const { isShowCEFwModal } = kFModel; | |||
| const [form] = Form.useForm(); | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <Modal title="Basic Modal" open={isShowCEFwModal} onOk={handleOk} onCancel={handleCancel}> | |||
| <Form | |||
| form={form} | |||
| name="validateOnly" | |||
| labelCol={{ span: 8 }} | |||
| wrapperCol={{ span: 16 }} | |||
| style={{ maxWidth: 600 }} | |||
| autoComplete="off" | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="文件名" | |||
| name="name" | |||
| rules={[{ required: true, message: 'Please input value!' }]} | |||
| > | |||
| <Input /> | |||
| </Form.Item> | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: false, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleOk = async () => { | |||
| try { | |||
| const values = await form.validateFields(); | |||
| const retcode = await dispatch<any>({ | |||
| type: 'kFModel/document_create', | |||
| payload: { | |||
| name: values.name, | |||
| kb_id, | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| getKfList && getKfList(); | |||
| } | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }; | |||
| </Form> | |||
| </Modal > | |||
| ); | |||
| } | |||
| export default connect(({ kFModel, loading }) => ({ kFModel, loading }))(Index); | |||
| return ( | |||
| <Modal | |||
| title="Basic Modal" | |||
| open={isShowCEFwModal} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| > | |||
| <Form | |||
| form={form} | |||
| name="validateOnly" | |||
| labelCol={{ span: 8 }} | |||
| wrapperCol={{ span: 16 }} | |||
| style={{ maxWidth: 600 }} | |||
| autoComplete="off" | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="文件名" | |||
| name="name" | |||
| rules={[{ required: true, message: 'Please input value!' }]} | |||
| > | |||
| <Input /> | |||
| </Form.Item> | |||
| </Form> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default FileCreatingModal; | |||
| @@ -1,228 +1,273 @@ | |||
| import React, { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { connect, Dispatch, useNavigate } from 'umi' | |||
| import { Space, Table, Input, Button, Switch, Dropdown, } from 'antd'; | |||
| import { getOneNamespaceEffectsLoading } from '@/utils/stroreUtil'; | |||
| import { DownOutlined } from '@ant-design/icons'; | |||
| import type { MenuProps } from 'antd'; | |||
| import { DownOutlined } from '@ant-design/icons' | |||
| import { debounce } from 'lodash'; | |||
| import { Button, Dropdown, Input, Space, Switch, Table } from 'antd'; | |||
| import type { ColumnsType } from 'antd/es/table'; | |||
| import UploadFile from './upload' | |||
| import CreateEPModal from './createEFileModal' | |||
| import SegmentSetModal from './segmentSetModal' | |||
| import styles from './index.less' | |||
| import type { kFModelState } from './model' | |||
| import { debounce } from 'lodash'; | |||
| import React, { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| import CreateEPModal from './createEFileModal'; | |||
| import styles from './index.less'; | |||
| import SegmentSetModal from './segmentSetModal'; | |||
| import UploadFile from './upload'; | |||
| interface DataType { | |||
| name: string; | |||
| chunk_num: string; | |||
| token_num: number; | |||
| update_date: string; | |||
| size: string; | |||
| status: string; | |||
| id: string; | |||
| parser_id: string | |||
| name: string; | |||
| chunk_num: string; | |||
| token_num: number; | |||
| update_date: string; | |||
| size: string; | |||
| status: string; | |||
| id: string; | |||
| parser_id: string; | |||
| } | |||
| interface kFProps { | |||
| dispatch: Dispatch; | |||
| kFModel: kFModelState; | |||
| kb_id: string | |||
| interface KFProps { | |||
| kb_id: string; | |||
| } | |||
| const Index: React.FC<kFProps> = ({ kFModel, dispatch, kb_id }) => { | |||
| const { data, loading } = kFModel | |||
| const [inputValue, setInputValue] = useState('') | |||
| const [doc_id, setDocId] = useState('0') | |||
| const [parser_id, setParserId] = useState('0') | |||
| let navigate = useNavigate(); | |||
| const getKfList = (keywords?: string) => { | |||
| const payload = { | |||
| kb_id, | |||
| keywords | |||
| } | |||
| if (!keywords) { | |||
| delete payload.keywords | |||
| } | |||
| dispatch({ | |||
| type: 'kFModel/getKfList', | |||
| payload | |||
| }); | |||
| } | |||
| useEffect(() => { | |||
| if (kb_id) { | |||
| getKfList() | |||
| } | |||
| }, [kb_id]) | |||
| const debounceChange = debounce(getKfList, 300) | |||
| const debounceCallback = useCallback((value: string) => debounceChange(value), []) | |||
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |||
| const value = e.target.value | |||
| setInputValue(value) | |||
| debounceCallback(e.target.value) | |||
| const KnowledgeFile: React.FC<KFProps> = ({ kb_id }) => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const effects = useSelector((state: any) => state.loading.effects); | |||
| const { data } = kFModel; | |||
| const loading = getOneNamespaceEffectsLoading('kFModel', effects, [ | |||
| 'getKfList', | |||
| 'updateDocumentStatus', | |||
| ]); | |||
| const [inputValue, setInputValue] = useState(''); | |||
| const [doc_id, setDocId] = useState('0'); | |||
| const [parser_id, setParserId] = useState('0'); | |||
| let navigate = useNavigate(); | |||
| const 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> | |||
| <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; | |||
| @@ -1,57 +1,47 @@ | |||
| import kbService from '@/services/kbService'; | |||
| import { message } from 'antd'; | |||
| import { Effect, Reducer, Subscription } from 'umi'; | |||
| import pick from 'lodash/pick'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface kFModelState { | |||
| export interface KFModelState { | |||
| isShowCEFwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| isShowSegmentSetModal: boolean; | |||
| loading: boolean; | |||
| tenantIfo: any; | |||
| data: any[]; | |||
| } | |||
| export interface kFModelType { | |||
| namespace: 'kFModel'; | |||
| state: kFModelState; | |||
| effects: { | |||
| createKf: Effect; | |||
| updateKf: Effect; | |||
| getKfDetail: Effect; | |||
| getKfList: Effect; | |||
| updateDocumentStatus: Effect; | |||
| document_rm: Effect; | |||
| document_create: Effect; | |||
| document_change_parser: Effect; | |||
| }; | |||
| reducers: { | |||
| updateState: Reducer<kFModelState>; | |||
| }; | |||
| subscriptions: { setup: Subscription }; | |||
| } | |||
| const Model: kFModelType = { | |||
| const model: DvaModel<KFModelState> = { | |||
| namespace: 'kFModel', | |||
| state: { | |||
| isShowCEFwModal: false, | |||
| isShowTntModal: false, | |||
| isShowSegmentSetModal: false, | |||
| loading: false, | |||
| tenantIfo: {}, | |||
| data: [], | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen((location) => {}); | |||
| }, | |||
| }, | |||
| effects: { | |||
| *createKf({ payload = {}, callback }, { call, put }) { | |||
| *createKf({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.createKb, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| message.success('创建成功!'); | |||
| } | |||
| }, | |||
| *updateKf({ payload = {}, callback }, { call, put }) { | |||
| *updateKf({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.updateKb, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| @@ -67,23 +57,12 @@ const Model: kFModelType = { | |||
| } | |||
| }, | |||
| *getKfList({ payload = {} }, { call, put }) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: true, | |||
| }, | |||
| }); | |||
| const { data, response } = yield call( | |||
| kbService.get_document_list, | |||
| payload, | |||
| ); | |||
| const { retcode, data: res, retmsg } = data; | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: false, | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| @@ -93,64 +72,64 @@ const Model: kFModelType = { | |||
| }); | |||
| } | |||
| }, | |||
| *updateDocumentStatus({ payload = {}, callback }, { call, put }) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: true, | |||
| }, | |||
| }); | |||
| *updateDocumentStatus({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call( | |||
| kbService.document_change_status, | |||
| payload, | |||
| pick(payload, ['doc_id', 'status']), | |||
| ); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| message.success('修改成功!'); | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: false, | |||
| }, | |||
| put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| callback && callback(); | |||
| } | |||
| }, | |||
| *document_rm({ payload = {}, callback }, { call, put }) { | |||
| const { data, response } = yield call(kbService.document_rm, payload); | |||
| *document_rm({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.document_rm, { | |||
| doc_id: payload.doc_id, | |||
| }); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| message.success('删除成功!'); | |||
| callback && callback(); | |||
| put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| } | |||
| }, | |||
| *document_create({ payload = {}, callback }, { call, put }) { | |||
| *document_create({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.document_create, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| put({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: false, | |||
| }, | |||
| }); | |||
| message.success('创建成功!'); | |||
| callback && callback(); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *document_change_parser({ payload = {}, callback }, { call, put }) { | |||
| *document_change_parser({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call( | |||
| kbService.document_change_parser, | |||
| payload, | |||
| ); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: false, | |||
| }, | |||
| }); | |||
| message.success('修改成功!'); | |||
| callback && callback(); | |||
| } | |||
| }, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| return retcode; | |||
| }, | |||
| }, | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -1,91 +1,87 @@ | |||
| import React from 'react'; | |||
| import { connect, Dispatch } from 'umi'; | |||
| import i18n from 'i18next'; | |||
| import { useTranslation, } from 'react-i18next' | |||
| import { Modal, Tag, Space } from 'antd' | |||
| import { useEffect, useState } from 'react'; | |||
| import { Modal, Space, Tag } from 'antd'; | |||
| import React, { useEffect, useState } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| import styles from './index.less'; | |||
| import type { kFModelState } from './model' | |||
| import type { settingModelState } from '@/pages/setting/model' | |||
| const { CheckableTag } = Tag; | |||
| interface kFProps { | |||
| dispatch: Dispatch; | |||
| kFModel: kFModelState; | |||
| settingModel: settingModelState; | |||
| getKfList: () => void; | |||
| parser_id: string; | |||
| doc_id: string; | |||
| getKfList: () => void; | |||
| parser_id: string; | |||
| doc_id: string; | |||
| } | |||
| const Index: React.FC<kFProps> = ({ kFModel, settingModel, dispatch, getKfList, parser_id, doc_id }) => { | |||
| const [selectedTag, setSelectedTag] = useState('') | |||
| const { tenantIfo = {} } = settingModel | |||
| const { parser_ids = '' } = tenantIfo | |||
| useEffect(() => { | |||
| dispatch({ | |||
| type: 'settingModel/getTenantInfo', | |||
| payload: { | |||
| } | |||
| }); | |||
| setSelectedTag(parser_id) | |||
| }, [parser_id]) | |||
| const { isShowSegmentSetModal } = kFModel | |||
| const { t } = useTranslation() | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: false | |||
| } | |||
| }); | |||
| }; | |||
| const handleOk = () => { | |||
| console.log(1111, selectedTag) | |||
| dispatch({ | |||
| type: 'kFModel/document_change_parser', | |||
| payload: { | |||
| parser_id: selectedTag, | |||
| doc_id | |||
| }, | |||
| callback: () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: false | |||
| } | |||
| }); | |||
| getKfList && getKfList() | |||
| } | |||
| }); | |||
| }; | |||
| const SegmentSetModal: React.FC<kFProps> = ({ | |||
| getKfList, | |||
| parser_id, | |||
| doc_id, | |||
| }) => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const settingModel = useSelector((state: any) => state.settingModel); | |||
| const [selectedTag, setSelectedTag] = useState(''); | |||
| const { tenantIfo = {} } = settingModel; | |||
| const { parser_ids = '' } = tenantIfo; | |||
| const { isShowSegmentSetModal } = kFModel; | |||
| const { t } = useTranslation(); | |||
| const handleChange = (tag: string, checked: boolean) => { | |||
| const nextSelectedTag = checked | |||
| ? tag | |||
| : selectedTag; | |||
| console.log('You are interested in: ', nextSelectedTag); | |||
| setSelectedTag(nextSelectedTag); | |||
| }; | |||
| useEffect(() => { | |||
| dispatch({ | |||
| type: 'settingModel/getTenantInfo', | |||
| payload: {}, | |||
| }); | |||
| setSelectedTag(parser_id); | |||
| }, [parser_id]); | |||
| return ( | |||
| <Modal title="Basic Modal" open={isShowSegmentSetModal} onOk={handleOk} onCancel={handleCancel}> | |||
| <Space size={[0, 8]} wrap> | |||
| <div className={styles.tags}> | |||
| { | |||
| parser_ids.split(',').map((tag: string) => { | |||
| return (<CheckableTag | |||
| key={tag} | |||
| checked={selectedTag === tag} | |||
| onChange={(checked) => handleChange(tag, checked)} | |||
| > | |||
| {tag} | |||
| </CheckableTag>) | |||
| }) | |||
| } | |||
| </div> | |||
| </Space> | |||
| </Modal > | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: false, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleOk = async () => { | |||
| console.log(1111, selectedTag); | |||
| const retcode = await dispatch<any>({ | |||
| type: 'kFModel/document_change_parser', | |||
| payload: { | |||
| parser_id: selectedTag, | |||
| doc_id, | |||
| }, | |||
| }); | |||
| ); | |||
| } | |||
| export default connect(({ kFModel, settingModel, loading }) => ({ kFModel, settingModel, loading }))(Index); | |||
| retcode === 0 && getKfList && getKfList(); | |||
| }; | |||
| const handleChange = (tag: string, checked: boolean) => { | |||
| const nextSelectedTag = checked ? tag : selectedTag; | |||
| console.log('You are interested in: ', nextSelectedTag); | |||
| setSelectedTag(nextSelectedTag); | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="Basic Modal" | |||
| open={isShowSegmentSetModal} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| > | |||
| <Space size={[0, 8]} wrap> | |||
| <div className={styles.tags}> | |||
| {parser_ids.split(',').map((tag: string) => { | |||
| return ( | |||
| <CheckableTag | |||
| key={tag} | |||
| checked={selectedTag === tag} | |||
| onChange={(checked) => handleChange(tag, checked)} | |||
| > | |||
| {tag} | |||
| </CheckableTag> | |||
| ); | |||
| })} | |||
| </div> | |||
| </Space> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default SegmentSetModal; | |||
| @@ -1,33 +1,39 @@ | |||
| import React from 'react'; | |||
| import { connect } from 'umi' | |||
| import uploadService from '@/services/uploadService'; | |||
| import type { UploadProps } from 'antd'; | |||
| import { Button, Upload } from 'antd'; | |||
| import uploadService from '@/services/uploadService' | |||
| import React from 'react'; | |||
| interface PropsType { | |||
| kb_id: string; | |||
| getKfList: () => void | |||
| kb_id: string; | |||
| getKfList: () => void; | |||
| } | |||
| type UploadRequestOption = Parameters< | |||
| NonNullable<UploadProps["customRequest"]> | |||
| NonNullable<UploadProps['customRequest']> | |||
| >[0]; | |||
| const Index: React.FC<PropsType> = ({ kb_id, getKfList }) => { | |||
| const createRequest: (props: UploadRequestOption) => void = async function ({ file, onSuccess, onError }) { | |||
| const { retcode, data } = await uploadService.uploadFile(file, kb_id); | |||
| if (retcode === 0) { | |||
| onSuccess && onSuccess(data, file); | |||
| } else { | |||
| onError && onError(data); | |||
| } | |||
| getKfList && getKfList() | |||
| }; | |||
| const uploadProps: UploadProps = { | |||
| customRequest: createRequest, | |||
| showUploadList: false, | |||
| }; | |||
| return (<Upload {...uploadProps} > | |||
| <Button type="link">导入文件</Button> | |||
| </Upload>) | |||
| } | |||
| const FileUpload: React.FC<PropsType> = ({ kb_id, getKfList }) => { | |||
| const createRequest: (props: UploadRequestOption) => void = async function ({ | |||
| file, | |||
| onSuccess, | |||
| onError, | |||
| }) { | |||
| const { retcode, data } = await uploadService.uploadFile(file, kb_id); | |||
| if (retcode === 0) { | |||
| onSuccess && onSuccess(data, file); | |||
| } else { | |||
| onError && onError(data); | |||
| } | |||
| getKfList && getKfList(); | |||
| }; | |||
| const uploadProps: UploadProps = { | |||
| customRequest: createRequest, | |||
| showUploadList: false, | |||
| }; | |||
| return ( | |||
| <Upload {...uploadProps}> | |||
| <Button type="link">导入文件</Button> | |||
| </Upload> | |||
| ); | |||
| }; | |||
| export default connect(({ kFModel, settingModel, loading }) => ({ kFModel, settingModel, loading }))(Index); | |||
| export default FileUpload; | |||
| @@ -1,247 +1,278 @@ | |||
| import React, { useEffect, useState, useCallback, } from 'react'; | |||
| import { useNavigate, connect, Dispatch } from 'umi' | |||
| import { Card, Row, Col, Input, Select, Switch, Pagination, Spin, Button, Popconfirm } from 'antd'; | |||
| import { MinusSquareOutlined, DeleteOutlined, } from '@ant-design/icons'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons'; | |||
| import type { PaginationProps } from 'antd'; | |||
| import { api_host } from '@/utils/api' | |||
| import CreateModal from '../knowledge-chunk/components/createModal' | |||
| import { | |||
| Card, | |||
| Col, | |||
| Input, | |||
| Pagination, | |||
| Popconfirm, | |||
| Row, | |||
| Select, | |||
| Spin, | |||
| Switch, | |||
| } from 'antd'; | |||
| import React, { useCallback, useEffect } from 'react'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| import CreateModal from '../knowledge-chunk/components/createModal'; | |||
| import styles from './index.less' | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | |||
| import { debounce } from 'lodash'; | |||
| import type { kSearchModelState } from './model' | |||
| import type { chunkModelState } from '../knowledge-chunk/model' | |||
| import styles from './index.less'; | |||
| interface chunkProps { | |||
| dispatch: Dispatch; | |||
| kSearchModel: kSearchModelState; | |||
| chunkModel: chunkModelState; | |||
| kb_id: string | |||
| kb_id: string; | |||
| } | |||
| const Index: React.FC<chunkProps> = ({ kSearchModel, chunkModel, dispatch, kb_id }) => { | |||
| const { data = [], total, loading, d_list = [], question, doc_ids, pagination, } = kSearchModel | |||
| const { chunk_id, doc_id, isShowCreateModal } = chunkModel | |||
| const KnowledgeSearching: React.FC<chunkProps> = ({ kb_id }) => { | |||
| const dispatch = useDispatch(); | |||
| const kSearchModel = useSelector((state: any) => state.kSearchModel); | |||
| const chunkModel = useSelector((state: any) => state.chunkModel); | |||
| const loading = useOneNamespaceEffectsLoading('kSearchModel', [ | |||
| 'chunk_list', | |||
| 'switch_chunk', | |||
| ]); | |||
| const { | |||
| data = [], | |||
| total, | |||
| d_list = [], | |||
| question, | |||
| doc_ids, | |||
| pagination, | |||
| } = kSearchModel; | |||
| const { chunk_id, doc_id, isShowCreateModal } = chunkModel; | |||
| const getChunkList = () => { | |||
| dispatch({ | |||
| type: 'kSearchModel/updateState', | |||
| payload: { | |||
| loading: true | |||
| } | |||
| }); | |||
| interface payloadType { | |||
| kb_id: string; | |||
| question?: string; | |||
| doc_ids: any[]; | |||
| similarity_threshold?: number | |||
| } | |||
| const payload: payloadType = { | |||
| kb_id, | |||
| question, | |||
| doc_ids, | |||
| similarity_threshold: 0.1 | |||
| } | |||
| dispatch({ | |||
| type: 'kSearchModel/chunk_list', | |||
| payload: { | |||
| ...payload, | |||
| ...pagination | |||
| } | |||
| kb_id, | |||
| }, | |||
| }); | |||
| } | |||
| }; | |||
| const confirm = (id: string) => { | |||
| console.log(id) | |||
| dispatch({ | |||
| type: 'kSearchModel/rm_chunk', | |||
| payload: { | |||
| chunk_ids: [id] | |||
| chunk_ids: [id], | |||
| kb_id, | |||
| }, | |||
| callback: getChunkList | |||
| }); | |||
| }; | |||
| const handleEditchunk = (item: any) => { | |||
| const { chunk_id, doc_id } = item | |||
| const { chunk_id, doc_id } = item; | |||
| dispatch({ | |||
| type: 'chunkModel/updateState', | |||
| payload: { | |||
| isShowCreateModal: true, | |||
| chunk_id, | |||
| doc_id | |||
| doc_id, | |||
| }, | |||
| callback: getChunkList | |||
| }); | |||
| } | |||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = (page, size) => { | |||
| getChunkList(); | |||
| }; | |||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = ( | |||
| page, | |||
| size, | |||
| ) => { | |||
| dispatch({ | |||
| type: 'kSearchModel/updateState', | |||
| payload: { | |||
| pagination: { page, size } | |||
| } | |||
| pagination: { page, size }, | |||
| }, | |||
| }); | |||
| }; | |||
| useEffect(() => { | |||
| dispatch({ | |||
| type: 'kSearchModel/updateState', | |||
| payload: { | |||
| loading: false, | |||
| doc_ids: [], | |||
| question: "" | |||
| } | |||
| question: '', | |||
| }, | |||
| }); | |||
| dispatch({ | |||
| type: 'kSearchModel/getKfList', | |||
| payload: { | |||
| kb_id | |||
| } | |||
| kb_id, | |||
| }, | |||
| }); | |||
| }, []) | |||
| }, []); | |||
| const switchChunk = (item: any, available_int: boolean) => { | |||
| const { chunk_id, doc_id } = item | |||
| dispatch({ | |||
| type: 'kSearchModel/updateState', | |||
| payload: { | |||
| loading: true | |||
| } | |||
| }); | |||
| const { chunk_id, doc_id } = item; | |||
| dispatch({ | |||
| type: 'kSearchModel/switch_chunk', | |||
| payload: { | |||
| chunk_ids: [chunk_id], | |||
| doc_id, | |||
| available_int | |||
| available_int, | |||
| kb_id, | |||
| }, | |||
| callback: getChunkList | |||
| }); | |||
| } | |||
| }; | |||
| useEffect(() => { | |||
| getChunkList() | |||
| }, [doc_ids, pagination, question]) | |||
| getChunkList(); | |||
| }, [doc_ids, pagination, question]); | |||
| const debounceChange = debounce((value) => { | |||
| dispatch({ | |||
| type: 'kSearchModel/updateState', | |||
| payload: { | |||
| question: value | |||
| } | |||
| question: value, | |||
| }, | |||
| }); | |||
| }, 300) | |||
| const debounceCallback = useCallback((value: string) => debounceChange(value), []) | |||
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |||
| const value = e.target.value | |||
| debounceCallback(value) | |||
| } | |||
| const handleSelectChange = (value: | |||
| any[]) => { | |||
| }, 300); | |||
| const debounceCallback = useCallback( | |||
| (value: string) => debounceChange(value), | |||
| [], | |||
| ); | |||
| const handleInputChange = ( | |||
| e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, | |||
| ) => { | |||
| const value = e.target.value; | |||
| debounceCallback(value); | |||
| }; | |||
| const handleSelectChange = (value: any[]) => { | |||
| dispatch({ | |||
| type: 'kSearchModel/updateState', | |||
| payload: { | |||
| doc_ids: value | |||
| } | |||
| doc_ids: value, | |||
| }, | |||
| }); | |||
| } | |||
| console.log('loading', loading) | |||
| return (<> | |||
| <div className={styles.chunkPage}> | |||
| <div className={styles.filter}> | |||
| <Select | |||
| showSearch | |||
| placeholder="文件列表" | |||
| optionFilterProp="children" | |||
| onChange={handleSelectChange} | |||
| style={{ width: 300, marginBottom: 20 }} | |||
| options={d_list} | |||
| fieldNames={{ label: 'name', value: 'id' }} | |||
| mode='multiple' | |||
| /> | |||
| }; | |||
| <Input.TextArea autoSize={{ minRows: 6, maxRows: 6 }} placeholder="搜索" style={{ width: 300 }} allowClear onChange={handleInputChange} /> | |||
| return ( | |||
| <> | |||
| <div className={styles.chunkPage}> | |||
| <div className={styles.filter}> | |||
| <Select | |||
| showSearch | |||
| placeholder="文件列表" | |||
| optionFilterProp="children" | |||
| onChange={handleSelectChange} | |||
| style={{ width: 300, marginBottom: 20 }} | |||
| options={d_list} | |||
| fieldNames={{ label: 'name', value: 'id' }} | |||
| mode="multiple" | |||
| /> | |||
| </div> | |||
| <div className={styles.pageContainer}> | |||
| <div className={styles.pageContent}> | |||
| <Spin spinning={loading} className={styles.spin} size='large'> | |||
| <Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24 }} > | |||
| { | |||
| data.map((item: any) => { | |||
| return (<Col className="gutter-row" key={item.chunk_id} xs={24} sm={12} md={12} lg={8}> | |||
| <Card className={styles.card} | |||
| onClick={() => { handleEditchunk(item) }} | |||
| <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 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 > | |||
| <CreateModal getChunkList={getChunkList} isShowCreateModal={isShowCreateModal} chunk_id={chunk_id} doc_id={doc_id} /> | |||
| </> | |||
| ) | |||
| <CreateModal | |||
| getChunkList={getChunkList} | |||
| isShowCreateModal={isShowCreateModal} | |||
| chunk_id={chunk_id} | |||
| doc_id={doc_id} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| export default connect(({ kSearchModel, chunkModel, loading }) => ({ kSearchModel, chunkModel, loading }))(Index); | |||
| export default KnowledgeSearching; | |||
| @@ -1,8 +1,8 @@ | |||
| import { Effect, Reducer, Subscription } from 'umi' | |||
| import { message } from 'antd'; | |||
| import kbService from '@/services/kbService'; | |||
| import omit from 'lodash/omit'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface kSearchModelState { | |||
| export interface KSearchModelState { | |||
| loading: boolean; | |||
| data: any[]; | |||
| total: number; | |||
| @@ -13,26 +13,10 @@ export interface kSearchModelState { | |||
| question: string; | |||
| doc_ids: any[]; | |||
| pagination: any; | |||
| doc_id: string | |||
| } | |||
| 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', | |||
| state: { | |||
| loading: false, | |||
| @@ -45,114 +29,132 @@ const Model: chunkgModelType = { | |||
| question: '', | |||
| doc_ids: [], | |||
| pagination: { page: 1, size: 30 }, | |||
| doc_id: '' | |||
| doc_id: '', | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen(location => { | |||
| console.log(location) | |||
| history.listen((location) => { | |||
| console.log(location); | |||
| }); | |||
| } | |||
| }, | |||
| }, | |||
| effects: { | |||
| *getKfList({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.get_document_list, payload); | |||
| const { data, response } = yield call( | |||
| kbService.get_document_list, | |||
| payload, | |||
| ); | |||
| const { retcode, data: res, retmsg } = data | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| d_list: res | |||
| } | |||
| d_list: res, | |||
| }, | |||
| }); | |||
| } | |||
| }, | |||
| * chunk_list({ payload = {}, callback }, { call, put }) { | |||
| const { data, response } = yield call(kbService.retrieval_test, payload); | |||
| const { retcode, data: res, retmsg } = data | |||
| *chunk_list({ payload = {} }, { call, put, select }) { | |||
| const { question, doc_ids, pagination }: KSearchModelState = yield select( | |||
| (state: any) => state.kSearchModel, | |||
| ); | |||
| const { data } = yield call(kbService.retrieval_test, { | |||
| ...payload, | |||
| ...pagination, | |||
| question, | |||
| doc_ids, | |||
| similarity_threshold: 0.1, | |||
| }); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| console.log(res) | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| data: res.chunks, | |||
| total: res.total, | |||
| loading: false | |||
| } | |||
| }, | |||
| }); | |||
| callback && callback() | |||
| } | |||
| }, | |||
| *switch_chunk({ payload = {}, callback }, { call, put }) { | |||
| const { data, response } = yield call(kbService.switch_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data | |||
| *switch_chunk({ payload = {} }, { call, put }) { | |||
| const { data } = yield call( | |||
| kbService.switch_chunk, | |||
| omit(payload, ['kb_id']), | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| callback && callback() | |||
| yield put({ | |||
| type: 'chunk_list', | |||
| payload: { | |||
| kb_id: payload.kb_id, | |||
| }, | |||
| }); | |||
| } | |||
| }, | |||
| *rm_chunk({ payload = {}, callback }, { call, put }) { | |||
| console.log('shanchu') | |||
| const { data, response } = yield call(kbService.rm_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data | |||
| *rm_chunk({ payload = {} }, { call, put }) { | |||
| const { data } = yield call(kbService.rm_chunk, { | |||
| chunk_ids: payload.chunk_ids, | |||
| }); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| callback && callback() | |||
| // TODO: Can be extracted | |||
| yield put({ | |||
| type: 'chunk_list', | |||
| payload: { | |||
| kb_id: payload.kb_id, | |||
| }, | |||
| }); | |||
| } | |||
| }, | |||
| * get_chunk({ payload = {}, callback }, { call, put }) { | |||
| *get_chunk({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.get_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| chunkInfo: res | |||
| } | |||
| chunkInfo: res, | |||
| }, | |||
| }); | |||
| callback && callback(res) | |||
| } | |||
| }, | |||
| *create_hunk({ payload = {} }, { call, put }) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: true | |||
| } | |||
| loading: true, | |||
| }, | |||
| }); | |||
| let service = kbService.create_chunk | |||
| let service = kbService.create_chunk; | |||
| if (payload.chunk_id) { | |||
| service = kbService.set_chunk | |||
| service = kbService.set_chunk; | |||
| } | |||
| const { data, response } = yield call(service, payload); | |||
| const { retcode, data: res, retmsg } = data | |||
| const { retcode, data: res, retmsg } = data; | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: false | |||
| } | |||
| loading: false, | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| isShowCreateModal: false | |||
| } | |||
| isShowCreateModal: false, | |||
| }, | |||
| }); | |||
| } | |||
| }, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload | |||
| }; | |||
| } | |||
| } | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -1,170 +1,157 @@ | |||
| import React, { useEffect, useState } from 'react'; | |||
| import { useNavigate, connect, Dispatch } from 'umi' | |||
| import { Button, Form, Input, Radio, Select, Tag, Space, } from 'antd'; | |||
| import type { kSModelState } from './model' | |||
| import type { settingModelState } from '@/pages/setting/model' | |||
| import styles from './index.less' | |||
| import { Button, Form, Input, Radio, Select, Space, Tag } from 'antd'; | |||
| import React, { useCallback, useEffect, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| import styles from './index.less'; | |||
| const { CheckableTag } = Tag; | |||
| const layout = { | |||
| labelCol: { span: 8 }, | |||
| wrapperCol: { span: 16 }, | |||
| labelAlign: 'left' as const | |||
| labelCol: { span: 8 }, | |||
| wrapperCol: { span: 16 }, | |||
| labelAlign: 'left' as const, | |||
| }; | |||
| const { Option } = Select | |||
| const { Option } = Select; | |||
| /* eslint-disable no-template-curly-in-string */ | |||
| interface kSProps { | |||
| dispatch: Dispatch; | |||
| kSModel: kSModelState; | |||
| settingModel: settingModelState; | |||
| kb_id: string | |||
| kb_id: string; | |||
| } | |||
| const Index: React.FC<kSProps> = ({ settingModel, kSModel, dispatch, kb_id }) => { | |||
| let navigate = useNavigate(); | |||
| const { tenantIfo = {} } = settingModel | |||
| const { parser_ids = '', embd_id = '' } = tenantIfo | |||
| const [form] = Form.useForm(); | |||
| const KnowledgeSetting: React.FC<kSProps> = ({ kb_id }) => { | |||
| const dispatch = useDispatch(); | |||
| const settingModel = useSelector((state: any) => state.settingModel); | |||
| let navigate = useNavigate(); | |||
| const { tenantIfo = {} } = settingModel; | |||
| const { parser_ids = '', embd_id = '' } = tenantIfo; | |||
| const [form] = Form.useForm(); | |||
| const [selectedTag, setSelectedTag] = useState(''); | |||
| const values = Form.useWatch([], form); | |||
| useEffect(() => { | |||
| 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> | |||
| <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 8 }}> | |||
| <Button type="primary" onClick={onFinish}> | |||
| 保存并处理 | |||
| </Button> | |||
| <Button htmlType="button" style={{ marginLeft: '20px' }}> | |||
| 取消 | |||
| </Button> | |||
| </Form.Item> | |||
| </Space> | |||
| <Space size={[0, 8]} wrap></Space> | |||
| <div className={styles.preset}> | |||
| <div className={styles.left}>xxxxx文章</div> | |||
| <div className={styles.right}>预估份数</div> | |||
| </div> | |||
| <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 8 }}> | |||
| <Button type="primary" onClick={onFinish}> | |||
| 保存并处理 | |||
| </Button> | |||
| <Button htmlType="button" style={{ marginLeft: '20px' }}> | |||
| 取消 | |||
| </Button> | |||
| </Form.Item> | |||
| </Form> | |||
| } | |||
| ); | |||
| }; | |||
| export default connect(({ settingModel, kSModel, loading }) => ({ settingModel, kSModel, loading }))(Index); | |||
| export default KnowledgeSetting; | |||
| @@ -1,72 +1,54 @@ | |||
| import { message } from 'antd'; | |||
| import { Effect, Reducer, Subscription } from 'umi' | |||
| import kbService from '@/services/kbService'; | |||
| import { message } from 'antd'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface kSModelState { | |||
| export interface KSModelState { | |||
| isShowPSwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| loading: boolean; | |||
| tenantIfo: any | |||
| } | |||
| 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', | |||
| state: { | |||
| isShowPSwModal: false, | |||
| isShowTntModal: false, | |||
| loading: false, | |||
| tenantIfo: {} | |||
| tenantIfo: {}, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen(location => { | |||
| }); | |||
| } | |||
| history.listen((location) => {}); | |||
| }, | |||
| }, | |||
| 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) { | |||
| 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) { | |||
| 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; | |||
| @@ -1,123 +1,120 @@ | |||
| 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 { 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; | |||
| @@ -1,6 +1,4 @@ | |||
| import { Effect, Reducer, Subscription } from 'umi' | |||
| import { message } from 'antd'; | |||
| import kbService from '@/services/kbService'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface kAModelState { | |||
| isShowPSwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| @@ -8,20 +6,10 @@ export interface kAModelState { | |||
| tenantIfo: any; | |||
| activeKey: 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', | |||
| state: { | |||
| isShowPSwModal: false, | |||
| @@ -30,25 +18,21 @@ const Model: kAModelType = { | |||
| tenantIfo: {}, | |||
| activeKey: 'setting', | |||
| id: '', | |||
| doc_id: '' | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen(location => { | |||
| }); | |||
| } | |||
| }, | |||
| effects: { | |||
| doc_id: '', | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload | |||
| ...payload, | |||
| }; | |||
| } | |||
| } | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen((location) => {}); | |||
| }, | |||
| }, | |||
| effects: {}, | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -1,15 +1,8 @@ | |||
| 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; | |||
| @@ -1,46 +1,32 @@ | |||
| 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; | |||
| @@ -1,51 +1,50 @@ | |||
| import React, { useEffect, useState } from 'react'; | |||
| import { UploadOutlined } from '@ant-design/icons'; | |||
| 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; | |||
| @@ -5,21 +5,22 @@ import { | |||
| PlusOutlined, | |||
| } from '@ant-design/icons'; | |||
| 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 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 [datas, setDatas] = useState(data) | |||
| const { data = [] } = knowledgeModel; | |||
| console.log(knowledgeModel); | |||
| // const x = useSelector((state) => state.knowledgeModel); | |||
| const fetchList = useCallback(() => { | |||
| dispatch({ | |||
| type: 'knowledgeModel/getList', | |||
| payload: {}, | |||
| }); | |||
| }, []); | |||
| const confirm = (id: string) => { | |||
| dispatch({ | |||
| @@ -27,12 +28,6 @@ const Index: React.FC<KnowledgeProps> = ({ knowledgeModel, dispatch }) => { | |||
| payload: { | |||
| kb_id: id, | |||
| }, | |||
| callback: () => { | |||
| dispatch({ | |||
| type: 'knowledgeModel/getList', | |||
| payload: {}, | |||
| }); | |||
| }, | |||
| }); | |||
| }; | |||
| const handleAddKnowledge = () => { | |||
| @@ -42,11 +37,8 @@ const Index: React.FC<KnowledgeProps> = ({ knowledgeModel, dispatch }) => { | |||
| navigate(`add/setting?activeKey=file&id=${id}`); | |||
| }; | |||
| useEffect(() => { | |||
| dispatch({ | |||
| type: 'knowledgeModel/getList', | |||
| payload: {}, | |||
| }); | |||
| }, []); | |||
| fetchList(); | |||
| }, [fetchList]); | |||
| return ( | |||
| <> | |||
| <div className={styles.knowledge}> | |||
| @@ -125,7 +117,4 @@ const Index: React.FC<KnowledgeProps> = ({ knowledgeModel, dispatch }) => { | |||
| ); | |||
| }; | |||
| export default connect(({ knowledgeModel, loading }) => ({ | |||
| knowledgeModel, | |||
| loading, | |||
| }))(Index); | |||
| export default Knowledge; | |||
| @@ -1,58 +1,38 @@ | |||
| import kbService from '@/services/kbService'; | |||
| import { Effect, Reducer } from 'umi'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface knowledgeModelState { | |||
| loading: boolean; | |||
| export interface KnowledgeModelState { | |||
| 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', | |||
| state: { | |||
| loading: false, | |||
| data: [], | |||
| }, | |||
| // subscriptions: { | |||
| // setup({ dispatch, history }) { | |||
| // history.listen((location) => { | |||
| // console.log(location); | |||
| // }); | |||
| // }, | |||
| // }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| effects: { | |||
| *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) { | |||
| callback && callback(); | |||
| yield put({ | |||
| type: 'getList', | |||
| payload: {}, | |||
| }); | |||
| } | |||
| }, | |||
| *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; | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| loading: false, | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| yield put({ | |||
| type: 'updateState', | |||
| @@ -63,13 +43,5 @@ const Model: knowledgegModelType = { | |||
| } | |||
| }, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -1,15 +1,20 @@ | |||
| import { rsaPsw } from '@/utils'; | |||
| 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'; | |||
| interface LoginProps { | |||
| dispatch: Dispatch; | |||
| } | |||
| const View: FC<LoginProps> = ({ dispatch }) => { | |||
| let navigate = useNavigate(); | |||
| const 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 = () => { | |||
| setTitle((title) => (title === 'login' ? 'register' : 'login')); | |||
| }; | |||
| @@ -26,27 +31,29 @@ const View: FC<LoginProps> = ({ dispatch }) => { | |||
| var rsaPassWord = rsaPsw(params.password); | |||
| if (title === 'login') { | |||
| const ret = await dispatch({ | |||
| const retcode = await dispatch<any>({ | |||
| type: 'loginModel/login', | |||
| payload: { | |||
| email: params.email, | |||
| password: rsaPassWord, | |||
| }, | |||
| }); | |||
| console.info(ret); | |||
| navigate('/knowledge'); | |||
| if (retcode === 0) { | |||
| navigate('/knowledge'); | |||
| } | |||
| } else { | |||
| dispatch({ | |||
| // TODO: Type needs to be improved | |||
| const retcode = await dispatch<any>({ | |||
| type: 'loginModel/register', | |||
| payload: { | |||
| nickname: params.nickname, | |||
| email: params.email, | |||
| password: rsaPassWord, | |||
| }, | |||
| callback() { | |||
| setTitle('login'); | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| setTitle('login'); | |||
| } | |||
| } | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| @@ -106,7 +113,7 @@ const View: FC<LoginProps> = ({ dispatch }) => { | |||
| label="Password" | |||
| rules={[{ required: true, message: 'Please input value' }]} | |||
| > | |||
| <Input size="large" placeholder="Please input value" /> | |||
| <Input.Password size="large" placeholder="Please input value" /> | |||
| </Form.Item> | |||
| {title === 'login' && ( | |||
| <Form.Item name="remember" valuePropName="checked"> | |||
| @@ -132,7 +139,13 @@ const View: FC<LoginProps> = ({ dispatch }) => { | |||
| </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'} | |||
| </Button> | |||
| {title === 'login' && ( | |||
| @@ -175,6 +188,4 @@ const View: FC<LoginProps> = ({ dispatch }) => { | |||
| ); | |||
| }; | |||
| export default connect(({ loginModel, loading }) => ({ loginModel, loading }))( | |||
| View, | |||
| ); | |||
| export default Login; | |||
| @@ -2,32 +2,29 @@ import { Authorization } from '@/constants/authorization'; | |||
| import userService from '@/services/userService'; | |||
| import authorizationUtil from '@/utils/authorizationUtil'; | |||
| import { message } from 'antd'; | |||
| import { Effect, Reducer, Subscription } from 'umi'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface loginModelState { | |||
| export interface LoginModelState { | |||
| list: any[]; | |||
| info: any; | |||
| 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', | |||
| state: { | |||
| list: [], | |||
| info: {}, | |||
| visible: false, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen((location) => {}); | |||
| @@ -53,29 +50,18 @@ const Model: logingModelType = { | |||
| userInfo: JSON.stringify(userInfo), | |||
| 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); | |||
| console.log(); | |||
| const { retcode, data: res, retmsg } = data; | |||
| if (retcode === 0) { | |||
| message.success('注册成功!'); | |||
| callback && callback(); | |||
| } | |||
| }, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| return retcode; | |||
| }, | |||
| }, | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -1,9 +1,9 @@ | |||
| import userService from '@/services/userService'; | |||
| import authorizationUtil from '@/utils/authorizationUtil'; | |||
| import { message } from 'antd'; | |||
| import { Effect, Reducer, Subscription } from 'umi'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface settingModelState { | |||
| export interface SettingModelState { | |||
| isShowPSwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| isShowSAKModal: boolean; | |||
| @@ -16,25 +16,7 @@ export interface settingModelState { | |||
| 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', | |||
| state: { | |||
| isShowPSwModal: false, | |||
| @@ -48,6 +30,14 @@ const Model: settingModelType = { | |||
| myLlm: [], | |||
| factoriesList: [], | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| subscriptions: { | |||
| setup({ dispatch, history }) { | |||
| history.listen((location) => {}); | |||
| @@ -176,13 +166,5 @@ const Model: settingModelType = { | |||
| } | |||
| }, | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| ...payload, | |||
| }; | |||
| }, | |||
| }, | |||
| }; | |||
| export default Model; | |||
| export default model; | |||
| @@ -19,101 +19,83 @@ const { | |||
| get_chunk, | |||
| switch_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; | |||
| @@ -3,55 +3,61 @@ import registerServer from '@/utils/registerServer'; | |||
| import request from '@/utils/request'; | |||
| 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; | |||
| @@ -1,17 +1,24 @@ | |||
| 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) { | |||
| server[key] = (params: any) => { | |||
| server[key] = (params) => { | |||
| if (opt[key].method === 'post' || opt[key].method === 'POST') { | |||
| return request(opt[key].url, { | |||
| method: opt[key].method, | |||
| data: params | |||
| data: params, | |||
| }); | |||
| } | |||
| if (opt[key].method === 'get' || opt[key].method === 'GET') { | |||
| return request.get(opt[key].url, { | |||
| params | |||
| params, | |||
| }); | |||
| } | |||
| }; | |||
| @@ -1,5 +1,5 @@ | |||
| import { message, notification } from 'antd'; | |||
| import { extend } from 'umi-request'; | |||
| import { RequestMethod, extend } from 'umi-request'; | |||
| import { Authorization } from '@/constants/authorization'; | |||
| import api from '@/utils/api'; | |||
| @@ -9,7 +9,7 @@ const { login } = api; | |||
| const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message | |||
| const retcodeMessage = { | |||
| const RetcodeMessage = { | |||
| 200: '服务器成功返回请求的数据。', | |||
| 201: '新建或修改数据成功。', | |||
| 202: '一个请求已经进入后台排队(异步任务)。', | |||
| @@ -26,7 +26,7 @@ const retcodeMessage = { | |||
| 503: '服务不可用,服务器暂时过载或维护。', | |||
| 504: '网关超时。', | |||
| }; | |||
| type retcode = | |||
| type ResultCode = | |||
| | 200 | |||
| | 201 | |||
| | 202 | |||
| @@ -45,7 +45,7 @@ type retcode = | |||
| /** | |||
| * 异常处理程序 | |||
| */ | |||
| interface responseType { | |||
| interface ResponseType { | |||
| retcode: number; | |||
| data: any; | |||
| retmsg: string; | |||
| @@ -62,7 +62,7 @@ const errorHandler = (error: { | |||
| } else { | |||
| if (response && response.status) { | |||
| const errorText = | |||
| retcodeMessage[response.status as retcode] || response.statusText; | |||
| RetcodeMessage[response.status as ResultCode] || response.statusText; | |||
| const { status, url } = response; | |||
| notification.error({ | |||
| message: `请求错误 ${status}: ${url}`, | |||
| @@ -81,7 +81,7 @@ const errorHandler = (error: { | |||
| /** | |||
| * 配置request请求时的默认参数 | |||
| */ | |||
| const request = extend({ | |||
| const request: RequestMethod = extend({ | |||
| errorHandler, // 默认错误处理 | |||
| timeout: 3000000, | |||
| getResponse: true, | |||
| @@ -108,7 +108,7 @@ request.interceptors.request.use((url: string, options: any) => { | |||
| request.interceptors.response.use(async (response: any, request) => { | |||
| console.log(response, request); | |||
| const data: responseType = await response.clone().json(); | |||
| const data: ResponseType = await response.clone().json(); | |||
| // response 拦截 | |||
| if (data.retcode === 401 || data.retcode === 401) { | |||
| @@ -0,0 +1,9 @@ | |||
| export const getOneNamespaceEffectsLoading = ( | |||
| namespace: string, | |||
| effects: Record<string, boolean>, | |||
| effectNames: Array<string>, | |||
| ) => { | |||
| return effectNames.some( | |||
| (effectName) => effects[`${namespace}/${effectName}`], | |||
| ); | |||
| }; | |||