diff --git a/web/src/hooks/storeHooks.ts b/web/src/hooks/storeHooks.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f69ce766eba7ae403e659396ff1dd2d5effc0e1 --- /dev/null +++ b/web/src/hooks/storeHooks.ts @@ -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); +}; diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/components/createModal.tsx b/web/src/pages/add-knowledge/components/knowledge-chunk/components/createModal.tsx index 8e9e8c37fa37617a689f29495dacbf159e2df2e8..c771ed35674ebfbe397f22defc93fbe60d7abdc6 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/components/createModal.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/components/createModal.tsx @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/components/editTag.tsx b/web/src/pages/add-knowledge/components/knowledge-chunk/components/editTag.tsx index c5a0878d06e2779e252206bbdebd320866a75845..261b408da4ca8f92e08212ba6894035747f02b6d 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/components/editTag.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/components/editTag.tsx @@ -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; \ No newline at end of file +export default EditTag; diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/index.tsx b/web/src/pages/add-knowledge/components/knowledge-chunk/index.tsx index 6f80e7f144a403458629cfbfc7bbbe438c876798..291876fad6bfd5c3cfd17a5272b55379c3c6fee8 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/index.tsx @@ -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); \ No newline at end of file +export default Chunk; diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/model.ts b/web/src/pages/add-knowledge/components/knowledge-chunk/model.ts index 06ce4412ec6167d004a972681c306a06d7f1ca62..2ebba51716703d9234c9a11be1821c91a886cdae 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/model.ts +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/model.ts @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/createEFileModal.tsx b/web/src/pages/add-knowledge/components/knowledge-file/createEFileModal.tsx index fc9fcf92f7c0ddd1731805ee3b26c2a1d92737bf..b26269d635c9db7baa04be6ec0059c74d403dfc2 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/createEFileModal.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/createEFileModal.tsx @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx index 866954f92b888c19bfd3cbcd3b8dbf88efc7b7ec..dfda404744cb2fb1307700e5371e796f2b1f2aaa 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx @@ -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); \ No newline at end of file +export default KnowledgeFile; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/model.ts b/web/src/pages/add-knowledge/components/knowledge-file/model.ts index 3923e651c0a8285d8e642d07976641c6fbc48551..d193b1a6215496696c70989ba24e68de93f3c764 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/model.ts +++ b/web/src/pages/add-knowledge/components/knowledge-file/model.ts @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/segmentSetModal.tsx b/web/src/pages/add-knowledge/components/knowledge-file/segmentSetModal.tsx index ae3881fbbe66031fef1ffabc37a18c4321f9d6a3..5da38b85b6a3ad5a1a7be2d26de48da23243c6f1 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/segmentSetModal.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/segmentSetModal.tsx @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/upload.tsx b/web/src/pages/add-knowledge/components/knowledge-file/upload.tsx index 60ae46e2b6d30abb94a2ea43c1950d371bc7077f..656c8d7360443f1e9dfd8e0b204ae1f0c03d3c96 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/upload.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/upload.tsx @@ -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); \ No newline at end of file +export default FileUpload; diff --git a/web/src/pages/add-knowledge/components/knowledge-search/index.tsx b/web/src/pages/add-knowledge/components/knowledge-search/index.tsx index a50fb0c3432780e60fe4b7516a99f487f5c10e8f..0635facb95461fa72929db9323fe97234659d04c 100644 --- a/web/src/pages/add-knowledge/components/knowledge-search/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-search/index.tsx @@ -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); \ No newline at end of file +export default KnowledgeSearching; diff --git a/web/src/pages/add-knowledge/components/knowledge-search/model.ts b/web/src/pages/add-knowledge/components/knowledge-search/model.ts index 247495e5bb4e3fa1cfd273c65c466d6febde3dc2..da9a4acae82bd68467f2d1118df795a6a8a0c927 100644 --- a/web/src/pages/add-knowledge/components/knowledge-search/model.ts +++ b/web/src/pages/add-knowledge/components/knowledge-search/model.ts @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-setting/index.tsx b/web/src/pages/add-knowledge/components/knowledge-setting/index.tsx index 67286f441f413c83d187f85cdd19de5af3b3fcef..e7b5659c9ebceaa03d84729930aaaf1c398eb8f1 100644 --- a/web/src/pages/add-knowledge/components/knowledge-setting/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-setting/index.tsx @@ -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); \ No newline at end of file +export default KnowledgeSetting; diff --git a/web/src/pages/add-knowledge/components/knowledge-setting/model.ts b/web/src/pages/add-knowledge/components/knowledge-setting/model.ts index 42e33a5b3e2c047e392f6765e6a224b2e3ffaa61..188477130671222a4fc235dc80b9f2f4fa42fc50 100644 --- a/web/src/pages/add-knowledge/components/knowledge-setting/model.ts +++ b/web/src/pages/add-knowledge/components/knowledge-setting/model.ts @@ -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; diff --git a/web/src/pages/add-knowledge/index.tsx b/web/src/pages/add-knowledge/index.tsx index 073a0c836043a0746ac54217487e68b6746d63ae..d2386a771c6aaebc07abbab21d24b4f57b34c4d1 100644 --- a/web/src/pages/add-knowledge/index.tsx +++ b/web/src/pages/add-knowledge/index.tsx @@ -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); \ No newline at end of file +export default KnowledgeAdding; diff --git a/web/src/pages/add-knowledge/model.ts b/web/src/pages/add-knowledge/model.ts index 6f1871bcf0e55ef2c5ce65022c3cab6ea4145014..32a75ef4da0741e20937dd3da1edb974cba0bc20 100644 --- a/web/src/pages/add-knowledge/model.ts +++ b/web/src/pages/add-knowledge/model.ts @@ -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; diff --git a/web/src/pages/chat/index.tsx b/web/src/pages/chat/index.tsx index 64445f6b493cfa0625f402149adf7b04dac1b3c6..f791322875d6e185c89e8f1f9e72eb599305b47e 100644 --- a/web/src/pages/chat/index.tsx +++ b/web/src/pages/chat/index.tsx @@ -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); \ No newline at end of file +export default Chat; diff --git a/web/src/pages/chat/model.ts b/web/src/pages/chat/model.ts index 103c978c9e0cd89d2a0ae5916c27e0986c686a48..6efdc52e38ff79d3821499e95277f5f0c0cf6c13 100644 --- a/web/src/pages/chat/model.ts +++ b/web/src/pages/chat/model.ts @@ -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; \ No newline at end of file +export default model; diff --git a/web/src/pages/file/index.tsx b/web/src/pages/file/index.tsx index a9310e3d9a84afe8e80349e69f26d1ac55ac3951..be7eada21b32db334e3b5710f282cad5506bf9d7 100644 --- a/web/src/pages/file/index.tsx +++ b/web/src/pages/file/index.tsx @@ -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; \ No newline at end of file +export default File; diff --git a/web/src/pages/knowledge/index.tsx b/web/src/pages/knowledge/index.tsx index cdf138b30bb9eebbe4529bb6299048201f549b20..8af444f9c086ae1a1976e2e5277faea20fd4a63e 100644 --- a/web/src/pages/knowledge/index.tsx +++ b/web/src/pages/knowledge/index.tsx @@ -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; diff --git a/web/src/pages/knowledge/model.ts b/web/src/pages/knowledge/model.ts index 83e30008196e5fa4fbb1d66996ce7e5371efe396..0621f763a38804df30158b558fb45cdb06b66bed 100644 --- a/web/src/pages/knowledge/model.ts +++ b/web/src/pages/knowledge/model.ts @@ -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; diff --git a/web/src/pages/login/index.tsx b/web/src/pages/login/index.tsx index 29edc3968549bd7b949edec7b07e12f254aab2c4..a0696b361b38cd893873ee801a409ad327121283 100644 --- a/web/src/pages/login/index.tsx +++ b/web/src/pages/login/index.tsx @@ -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; diff --git a/web/src/pages/login/model.ts b/web/src/pages/login/model.ts index 0ce6150c8fd2286dd9b7e367c83c7cdb7b326e01..257879f6dea386b7ecc921bc9bb62e2c8845b5d5 100644 --- a/web/src/pages/login/model.ts +++ b/web/src/pages/login/model.ts @@ -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; diff --git a/web/src/pages/setting/model.ts b/web/src/pages/setting/model.ts index 0971b31efc51d829cbe9f0a2a8df4f63f1b56c37..604b87fdacb7b25fc8483cf3b508915228ef525a 100644 --- a/web/src/pages/setting/model.ts +++ b/web/src/pages/setting/model.ts @@ -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; diff --git a/web/src/services/kbService.ts b/web/src/services/kbService.ts index 2ea4917988c700beca7c76123078cf141dfffe53..0fa02544baa3a20a922ac2a513ec6fe4ce7d6feb 100644 --- a/web/src/services/kbService.ts +++ b/web/src/services/kbService.ts @@ -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; diff --git a/web/src/services/userService.ts b/web/src/services/userService.ts index 37a7b3364610e66a7df24bf5eaf023043bf2fc76..6e11f7344d13b36cbb2df05b7120588a929bb428 100644 --- a/web/src/services/userService.ts +++ b/web/src/services/userService.ts @@ -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; diff --git a/web/src/utils/registerServer.ts b/web/src/utils/registerServer.ts index 148ce0814a717961ab8c6f52cb8a942cca6f0748..9404ce0d4264abaf2ab5b6a85ba96de9f7f739b3 100644 --- a/web/src/utils/registerServer.ts +++ b/web/src/utils/registerServer.ts @@ -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, }); } }; diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index 60fae733b0bc4e081c0a177245d0f0ee66093743..536c6746ea1c7dacb5f35cc1f671d79f827864d2 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -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) { diff --git a/web/src/utils/stroreUtil.ts b/web/src/utils/stroreUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..4437d25fcf03d65956a2b763eedb2282e9b438af --- /dev/null +++ b/web/src/utils/stroreUtil.ts @@ -0,0 +1,9 @@ +export const getOneNamespaceEffectsLoading = ( + namespace: string, + effects: Record<string, boolean>, + effectNames: Array<string>, +) => { + return effectNames.some( + (effectName) => effects[`${namespace}/${effectName}`], + ); +};