* feat: set chunk to available state * feat: select all chunktags/v0.1.0
| @@ -0,0 +1,5 @@ | |||
| // .eslintrc.js | |||
| module.exports = { | |||
| // Umi 项目 | |||
| extends: [require.resolve('umi/eslint'), 'plugin:react-hooks/recommended'], | |||
| }; | |||
| @@ -27,6 +27,7 @@ | |||
| "@types/lodash": "^4.14.202", | |||
| "@types/react": "^18.0.33", | |||
| "@types/react-dom": "^18.0.11", | |||
| "@umijs/lint": "^4.1.1", | |||
| "@umijs/plugins": "^4.1.0", | |||
| "cross-env": "^7.0.3", | |||
| "prettier": "^3.2.4", | |||
| @@ -3479,16 +3480,17 @@ | |||
| } | |||
| }, | |||
| "node_modules/@umijs/lint": { | |||
| "version": "4.1.0", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/lint/-/lint-4.1.0.tgz", | |||
| "integrity": "sha512-drXkAeBJGMLrPr/dDiOZ2Z+3VKkAf53MzoOIhwHy5atq+PFNG9Y7e6YuWrK3qVF75zg9culQzlHTvinCjDK97Q==", | |||
| "version": "4.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/lint/-/lint-4.1.1.tgz", | |||
| "integrity": "sha512-fy2edKuYw42eM3LuH/2AiH0ZKdembFx3SR8dIGKxf7BmEQOSfUhskLiNGE8tSRubCiVzGUWvZQDw1YQcU0bsHg==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "@babel/core": "7.23.6", | |||
| "@babel/eslint-parser": "7.23.3", | |||
| "@stylelint/postcss-css-in-js": "^0.38.0", | |||
| "@typescript-eslint/eslint-plugin": "^5.62.0", | |||
| "@typescript-eslint/parser": "^5.62.0", | |||
| "@umijs/babel-preset-umi": "4.1.0", | |||
| "@umijs/babel-preset-umi": "4.1.1", | |||
| "eslint-plugin-jest": "27.2.3", | |||
| "eslint-plugin-react": "7.33.2", | |||
| "eslint-plugin-react-hooks": "4.6.0", | |||
| @@ -3501,6 +3503,7 @@ | |||
| "version": "7.23.6", | |||
| "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.23.6.tgz", | |||
| "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "@ampproject/remapping": "^2.2.0", | |||
| "@babel/code-frame": "^7.23.5", | |||
| @@ -3522,6 +3525,54 @@ | |||
| "node": ">=6.9.0" | |||
| } | |||
| }, | |||
| "node_modules/@umijs/lint/node_modules/@babel/runtime": { | |||
| "version": "7.23.6", | |||
| "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.6.tgz", | |||
| "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "regenerator-runtime": "^0.14.0" | |||
| }, | |||
| "engines": { | |||
| "node": ">=6.9.0" | |||
| } | |||
| }, | |||
| "node_modules/@umijs/lint/node_modules/@umijs/babel-preset-umi": { | |||
| "version": "4.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/babel-preset-umi/-/babel-preset-umi-4.1.1.tgz", | |||
| "integrity": "sha512-6pYZnF03euAJGZN3VLe8PKKRNMH6Zxj4GKNooLvJ0Wz0eMufmYDcA4CpbR6h8i1JpgcQ0Sngr8bqHLb7oMqrvw==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "@babel/runtime": "7.23.6", | |||
| "@bloomberg/record-tuple-polyfill": "0.0.4", | |||
| "@umijs/bundler-utils": "4.1.1", | |||
| "@umijs/utils": "4.1.1", | |||
| "core-js": "3.34.0" | |||
| } | |||
| }, | |||
| "node_modules/@umijs/lint/node_modules/@umijs/bundler-utils": { | |||
| "version": "4.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/bundler-utils/-/bundler-utils-4.1.1.tgz", | |||
| "integrity": "sha512-k1I1tjDePgB1XqpQHZiLJ/5gS4EykY8hqqzEzD1CSbd5KFE614+q6W/gcpFZ0YLJDWY1GdjOYpRokvuI/MSRfg==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "@umijs/utils": "4.1.1", | |||
| "esbuild": "0.17.19", | |||
| "regenerate": "^1.4.2", | |||
| "regenerate-unicode-properties": "10.1.1", | |||
| "spdy": "^4.0.2" | |||
| } | |||
| }, | |||
| "node_modules/@umijs/lint/node_modules/@umijs/utils": { | |||
| "version": "4.1.1", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/utils/-/utils-4.1.1.tgz", | |||
| "integrity": "sha512-hbnbJR3RA7fu4E7q4JFZ47XMYArr6Zn5bftr8YZ+o6hzJlomr4gzoOXE+XxM7rVMK4AFZoc+QZgNTJyISd08Pg==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "chokidar": "3.5.3", | |||
| "pino": "7.11.0" | |||
| } | |||
| }, | |||
| "node_modules/@umijs/mfsu": { | |||
| "version": "4.1.0", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/mfsu/-/mfsu-4.1.0.tgz", | |||
| @@ -16319,6 +16370,31 @@ | |||
| "qs": "^6.9.1" | |||
| } | |||
| }, | |||
| "node_modules/umi/node_modules/@babel/core": { | |||
| "version": "7.23.6", | |||
| "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.23.6.tgz", | |||
| "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", | |||
| "dependencies": { | |||
| "@ampproject/remapping": "^2.2.0", | |||
| "@babel/code-frame": "^7.23.5", | |||
| "@babel/generator": "^7.23.6", | |||
| "@babel/helper-compilation-targets": "^7.23.6", | |||
| "@babel/helper-module-transforms": "^7.23.3", | |||
| "@babel/helpers": "^7.23.6", | |||
| "@babel/parser": "^7.23.6", | |||
| "@babel/template": "^7.22.15", | |||
| "@babel/traverse": "^7.23.6", | |||
| "@babel/types": "^7.23.6", | |||
| "convert-source-map": "^2.0.0", | |||
| "debug": "^4.1.0", | |||
| "gensync": "^1.0.0-beta.2", | |||
| "json5": "^2.2.3", | |||
| "semver": "^6.3.1" | |||
| }, | |||
| "engines": { | |||
| "node": ">=6.9.0" | |||
| } | |||
| }, | |||
| "node_modules/umi/node_modules/@babel/runtime": { | |||
| "version": "7.23.6", | |||
| "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.6.tgz", | |||
| @@ -16330,6 +16406,25 @@ | |||
| "node": ">=6.9.0" | |||
| } | |||
| }, | |||
| "node_modules/umi/node_modules/@umijs/lint": { | |||
| "version": "4.1.0", | |||
| "resolved": "https://registry.npmmirror.com/@umijs/lint/-/lint-4.1.0.tgz", | |||
| "integrity": "sha512-drXkAeBJGMLrPr/dDiOZ2Z+3VKkAf53MzoOIhwHy5atq+PFNG9Y7e6YuWrK3qVF75zg9culQzlHTvinCjDK97Q==", | |||
| "dependencies": { | |||
| "@babel/core": "7.23.6", | |||
| "@babel/eslint-parser": "7.23.3", | |||
| "@stylelint/postcss-css-in-js": "^0.38.0", | |||
| "@typescript-eslint/eslint-plugin": "^5.62.0", | |||
| "@typescript-eslint/parser": "^5.62.0", | |||
| "@umijs/babel-preset-umi": "4.1.0", | |||
| "eslint-plugin-jest": "27.2.3", | |||
| "eslint-plugin-react": "7.33.2", | |||
| "eslint-plugin-react-hooks": "4.6.0", | |||
| "postcss": "^8.4.21", | |||
| "postcss-syntax": "0.36.2", | |||
| "stylelint-config-standard": "25.0.0" | |||
| } | |||
| }, | |||
| "node_modules/umi/node_modules/fast-glob": { | |||
| "version": "3.3.2", | |||
| "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", | |||
| @@ -5,6 +5,7 @@ | |||
| "build": "umi build", | |||
| "dev": "cross-env PORT=9000 umi dev", | |||
| "postinstall": "umi setup", | |||
| "lint": "umi lint --eslint-only", | |||
| "setup": "umi setup", | |||
| "start": "npm run dev" | |||
| }, | |||
| @@ -30,6 +31,7 @@ | |||
| "@types/lodash": "^4.14.202", | |||
| "@types/react": "^18.0.33", | |||
| "@types/react-dom": "^18.0.11", | |||
| "@umijs/lint": "^4.1.1", | |||
| "@umijs/plugins": "^4.1.0", | |||
| "cross-env": "^7.0.3", | |||
| "prettier": "^3.2.4", | |||
| @@ -1,5 +1,5 @@ | |||
| import React, { ReactNode } from "react"; | |||
| import { Inspector } from "react-dev-inspector"; | |||
| import React, { ReactNode } from 'react'; | |||
| import { Inspector } from 'react-dev-inspector'; | |||
| export function rootContainer(container: ReactNode) { | |||
| return React.createElement(Inspector, null, container); | |||
| @@ -5,4 +5,5 @@ export interface Pagination { | |||
| export interface BaseState { | |||
| pagination: Pagination; | |||
| searchString: string; | |||
| } | |||
| @@ -65,3 +65,13 @@ export interface ITenantInfo { | |||
| chat_id: string; | |||
| speech2text_id: string; | |||
| } | |||
| export interface IChunk { | |||
| available_int: number; // Whether to enable, 0: not enabled, 1: enabled | |||
| chunk_id: string; | |||
| content_with_weight: string; | |||
| doc_id: string; | |||
| docnm_kwd: string; | |||
| img_id: string; | |||
| important_kwd: any[]; | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| .image { | |||
| width: 100px !important; | |||
| min-width: 100px; | |||
| } | |||
| .imagePreview { | |||
| width: 600px; | |||
| } | |||
| @@ -0,0 +1,88 @@ | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { Card, Checkbox, CheckboxProps, Flex, Popover, Switch } from 'antd'; | |||
| import { useDispatch } from 'umi'; | |||
| import { useState } from 'react'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| item: IChunk; | |||
| checked: boolean; | |||
| handleCheckboxClick: (chunkId: string, checked: boolean) => void; | |||
| } | |||
| interface IImage { | |||
| id: string; | |||
| className: string; | |||
| } | |||
| // Pass onMouseEnter and onMouseLeave to img tag using props | |||
| const Image = ({ id, className, ...props }: IImage) => { | |||
| return ( | |||
| <img | |||
| {...props} | |||
| src={`${api_host}/document/image/${id}`} | |||
| alt="" | |||
| className={className} | |||
| /> | |||
| ); | |||
| }; | |||
| const ChunkCard = ({ item, checked, handleCheckboxClick }: IProps) => { | |||
| const dispatch = useDispatch(); | |||
| const available = Number(item.available_int); | |||
| const [enabled, setEnabled] = useState(available === 1); | |||
| const switchChunk = () => { | |||
| dispatch({ | |||
| type: 'chunkModel/switch_chunk', | |||
| payload: { | |||
| chunk_ids: [item.chunk_id], | |||
| available_int: available === 0 ? 1 : 0, | |||
| doc_id: item.doc_id, | |||
| }, | |||
| }); | |||
| }; | |||
| const onChange = (checked: boolean) => { | |||
| setEnabled(checked); | |||
| switchChunk(); | |||
| }; | |||
| const handleCheck: CheckboxProps['onChange'] = (e) => { | |||
| handleCheckboxClick(item.chunk_id, e.target.checked); | |||
| }; | |||
| return ( | |||
| <div> | |||
| <Card> | |||
| <Flex gap={'middle'} justify={'space-between'}> | |||
| <Checkbox onChange={handleCheck} checked={checked}></Checkbox> | |||
| {item.img_id && ( | |||
| <Popover | |||
| placement="topRight" | |||
| content={ | |||
| <Image id={item.img_id} className={styles.imagePreview}></Image> | |||
| } | |||
| > | |||
| <img | |||
| src={`${api_host}/document/image/${item.img_id}`} | |||
| alt="" | |||
| className={styles.image} | |||
| /> | |||
| <Image id={item.img_id} className={styles.image}></Image> | |||
| </Popover> | |||
| )} | |||
| <section>{item.content_with_weight}</section> | |||
| <div> | |||
| <Switch checked={enabled} onChange={onChange} /> | |||
| </div> | |||
| </Flex> | |||
| </Card> | |||
| </div> | |||
| ); | |||
| }; | |||
| export default ChunkCard; | |||
| @@ -1,4 +1,6 @@ | |||
| import { ReactComponent as FilterIcon } from '@/assets/filter.svg'; | |||
| import { KnowledgeRouteKey } from '@/constants/knowledge'; | |||
| import { useKnowledgeBaseId } from '@/hooks/knowledgeHook'; | |||
| import { | |||
| ArrowLeftOutlined, | |||
| CheckCircleOutlined, | |||
| @@ -9,17 +11,50 @@ import { | |||
| PlusOutlined, | |||
| SearchOutlined, | |||
| } from '@ant-design/icons'; | |||
| import { Button, Checkbox, Flex, Menu, MenuProps, Popover, Space } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| import { | |||
| Button, | |||
| Checkbox, | |||
| Flex, | |||
| Menu, | |||
| MenuProps, | |||
| Popover, | |||
| Radio, | |||
| RadioChangeEvent, | |||
| Space, | |||
| } from 'antd'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { Link, useDispatch, useSelector } from 'umi'; | |||
| import { ChunkModelState } from '../../model'; | |||
| interface IProps { | |||
| checked: boolean; | |||
| getChunkList: () => void; | |||
| selectAllChunk: (checked: boolean) => void; | |||
| } | |||
| const ChunkToolBar = ({ getChunkList, selectAllChunk, checked }: IProps) => { | |||
| const { documentInfo, available }: ChunkModelState = useSelector( | |||
| (state: any) => state.chunkModel, | |||
| ); | |||
| const dispatch = useDispatch(); | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| const handleSelectAllCheck = useCallback( | |||
| (e: any) => { | |||
| // console.info(e.target.checked); | |||
| selectAllChunk(e.target.checked); | |||
| }, | |||
| [selectAllChunk], | |||
| ); | |||
| const ChunkToolBar = () => { | |||
| const items: MenuProps['items'] = useMemo(() => { | |||
| return [ | |||
| { | |||
| key: '1', | |||
| label: ( | |||
| <> | |||
| <Checkbox> | |||
| <Checkbox onChange={handleSelectAllCheck} checked={checked}> | |||
| <b>Select All</b> | |||
| </Checkbox> | |||
| </> | |||
| @@ -55,47 +90,49 @@ const ChunkToolBar = () => { | |||
| ), | |||
| }, | |||
| ]; | |||
| }, []); | |||
| }, [checked, handleSelectAllCheck]); | |||
| const content = ( | |||
| <Menu style={{ width: 200 }} items={items} selectable={false} /> | |||
| ); | |||
| const handleFilterChange = (e: RadioChangeEvent) => { | |||
| dispatch({ type: 'chunkModel/setAvailable', payload: e.target.value }); | |||
| getChunkList(); | |||
| }; | |||
| const filterContent = ( | |||
| <Radio.Group onChange={handleFilterChange} value={available}> | |||
| <Space direction="vertical"> | |||
| <Radio value={undefined}>All</Radio> | |||
| <Radio value={1}>Enabled</Radio> | |||
| <Radio value={0}>Disabled</Radio> | |||
| </Space> | |||
| </Radio.Group> | |||
| ); | |||
| return ( | |||
| <Flex justify="space-between" align="center"> | |||
| <Space> | |||
| <ArrowLeftOutlined /> | |||
| <Space size={'middle'}> | |||
| <Link | |||
| to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`} | |||
| > | |||
| <ArrowLeftOutlined /> | |||
| </Link> | |||
| <FilePdfOutlined /> | |||
| xxx.pdf | |||
| {documentInfo.name} | |||
| </Space> | |||
| <Space> | |||
| {/* <Select | |||
| defaultValue="lucy" | |||
| style={{ width: 100 }} | |||
| popupMatchSelectWidth={false} | |||
| optionRender={() => null} | |||
| dropdownRender={(menu) => ( | |||
| <div style={{ width: 300 }}> | |||
| {menu} | |||
| <Menu | |||
| // onClick={onClick} | |||
| style={{ width: 256 }} | |||
| // defaultSelectedKeys={['1']} | |||
| // defaultOpenKeys={['sub1']} | |||
| // mode="inline" | |||
| items={actionItems} | |||
| /> | |||
| </div> | |||
| )} | |||
| ></Select> */} | |||
| <Popover content={content} placement="bottomLeft" arrow={false}> | |||
| <Popover content={content} placement="bottom" arrow={false}> | |||
| <Button> | |||
| Bulk | |||
| <DownOutlined /> | |||
| </Button> | |||
| </Popover> | |||
| <Button icon={<SearchOutlined />} /> | |||
| <Button icon={<FilterIcon />} /> | |||
| <Popover content={filterContent} placement="bottom" arrow={false}> | |||
| <Button icon={<FilterIcon />} /> | |||
| </Popover> | |||
| <Button icon={<DeleteOutlined />} /> | |||
| <Button icon={<PlusOutlined />} type="primary" /> | |||
| </Space> | |||
| @@ -1,70 +1,69 @@ | |||
| .chunkPage { | |||
| padding: 24px; | |||
| padding: 24px; | |||
| display: flex; | |||
| height: calc(100vh - 112px); | |||
| flex-direction: column; | |||
| display: flex; | |||
| // height: calc(100vh - 112px); | |||
| flex-direction: column; | |||
| .filter { | |||
| margin: 10px 0; | |||
| display: flex; | |||
| height: 32px; | |||
| justify-content: space-between; | |||
| } | |||
| .filter { | |||
| margin: 10px 0; | |||
| display: flex; | |||
| height: 32px; | |||
| justify-content: space-between; | |||
| } | |||
| .pageContent { | |||
| flex: 1; | |||
| width: 100%; | |||
| padding-right: 12px; | |||
| overflow-y: auto; | |||
| .pageContent { | |||
| flex: 1; | |||
| width: 100%; | |||
| padding-right: 12px; | |||
| overflow-y: auto; | |||
| .spin { | |||
| min-height: 400px; | |||
| } | |||
| .spin { | |||
| min-height: 400px; | |||
| } | |||
| } | |||
| .pageFooter { | |||
| height: 32px; | |||
| } | |||
| .pageFooter { | |||
| height: 32px; | |||
| } | |||
| } | |||
| .container { | |||
| height: 100px; | |||
| height: 100px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| .content { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| .content { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| .context { | |||
| flex: 1; | |||
| // width: 207px; | |||
| height: 88px; | |||
| overflow: hidden; | |||
| } | |||
| .context { | |||
| flex: 1; | |||
| // width: 207px; | |||
| height: 88px; | |||
| overflow: hidden; | |||
| } | |||
| } | |||
| .footer { | |||
| height: 20px; | |||
| .footer { | |||
| height: 20px; | |||
| .text { | |||
| margin-left: 10px; | |||
| } | |||
| .text { | |||
| margin-left: 10px; | |||
| } | |||
| } | |||
| } | |||
| .card { | |||
| :global { | |||
| .ant-card-body { | |||
| padding: 10px; | |||
| margin: 0; | |||
| } | |||
| margin-bottom: 10px; | |||
| :global { | |||
| .ant-card-body { | |||
| padding: 10px; | |||
| margin: 0; | |||
| } | |||
| cursor: pointer; | |||
| margin-bottom: 10px; | |||
| } | |||
| } | |||
| cursor: pointer; | |||
| } | |||
| @@ -1,41 +1,36 @@ | |||
| import { api_host } from '@/utils/api'; | |||
| import { getOneNamespaceEffectsLoading } from '@/utils/storeUtil'; | |||
| import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons'; | |||
| import type { PaginationProps } from 'antd'; | |||
| import { | |||
| Button, | |||
| Card, | |||
| Col, | |||
| Input, | |||
| Pagination, | |||
| Popconfirm, | |||
| Row, | |||
| Select, | |||
| Spin, | |||
| Switch, | |||
| } from 'antd'; | |||
| import { Button, Input, Pagination, Space, Spin } from 'antd'; | |||
| import { debounce } from 'lodash'; | |||
| import React, { useCallback, useEffect, useState } from 'react'; | |||
| import { useDispatch, useSearchParams, useSelector } from 'umi'; | |||
| import CreateModal from './components/createModal'; | |||
| import ChunkCard from './components/chunk-card'; | |||
| import ChunkToolBar from './components/chunk-toolbar'; | |||
| import styles from './index.less'; | |||
| import { ChunkModelState } from './model'; | |||
| interface PayloadType { | |||
| doc_id: string; | |||
| keywords?: string; | |||
| available_int?: number; | |||
| } | |||
| const Chunk = () => { | |||
| const dispatch = useDispatch(); | |||
| const chunkModel = useSelector((state: any) => state.chunkModel); | |||
| const chunkModel: ChunkModelState = useSelector( | |||
| (state: any) => state.chunkModel, | |||
| ); | |||
| const [keywords, SetKeywords] = useState(''); | |||
| const [available_int, setAvailableInt] = useState(-1); | |||
| const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]); | |||
| const [searchParams] = useSearchParams(); | |||
| const [pagination, setPagination] = useState({ page: 1, size: 30 }); | |||
| const { data = [], total, chunk_id, isShowCreateModal } = chunkModel; | |||
| const { | |||
| data = [], | |||
| total, | |||
| chunk_id, | |||
| isShowCreateModal, | |||
| pagination, | |||
| } = chunkModel; | |||
| const effects = useSelector((state: any) => state.loading.effects); | |||
| const loading = getOneNamespaceEffectsLoading('chunkModel', effects, [ | |||
| 'create_hunk', | |||
| @@ -44,23 +39,19 @@ const Chunk = () => { | |||
| ]); | |||
| const documentId: string = searchParams.get('doc_id') || ''; | |||
| const getChunkList = (value?: string) => { | |||
| const getChunkList = () => { | |||
| const payload: PayloadType = { | |||
| doc_id: documentId, | |||
| keywords: value || keywords, | |||
| available_int, | |||
| }; | |||
| if (payload.available_int === -1) { | |||
| delete payload.available_int; | |||
| } | |||
| dispatch({ | |||
| type: 'chunkModel/chunk_list', | |||
| payload: { | |||
| ...payload, | |||
| ...pagination, | |||
| }, | |||
| }); | |||
| }; | |||
| const confirm = async (id: string) => { | |||
| const retcode = await dispatch<any>({ | |||
| type: 'chunkModel/rm_chunk', | |||
| @@ -84,29 +75,55 @@ const Chunk = () => { | |||
| getChunkList(); | |||
| }; | |||
| const onShowSizeChange: PaginationProps['onShowSizeChange'] = ( | |||
| const onPaginationChange: PaginationProps['onShowSizeChange'] = ( | |||
| page, | |||
| size, | |||
| ) => { | |||
| setPagination({ page, size }); | |||
| }; | |||
| const switchChunk = async (id: string, available_int: boolean) => { | |||
| const retcode = await dispatch<any>({ | |||
| type: 'chunkModel/switch_chunk', | |||
| setSelectedChunkIds([]); | |||
| dispatch({ | |||
| type: 'chunkModel/setPagination', | |||
| payload: { | |||
| chunk_ids: [id], | |||
| available_int: Number(available_int), | |||
| doc_id: documentId, | |||
| current: page, | |||
| pageSize: size, | |||
| }, | |||
| }); | |||
| retcode === 0 && getChunkList(); | |||
| getChunkList(); | |||
| }; | |||
| const selectAllChunk = useCallback( | |||
| (checked: boolean) => { | |||
| setSelectedChunkIds(checked ? data.map((x) => x.chunk_id) : []); | |||
| // setSelectedChunkIds((previousIds) => { | |||
| // return checked ? [...previousIds, ...data.map((x) => x.chunk_id)] : []; | |||
| // }); | |||
| }, | |||
| [data], | |||
| ); | |||
| const handleSingleCheckboxClick = useCallback( | |||
| (chunkId: string, checked: boolean) => { | |||
| setSelectedChunkIds((previousIds) => { | |||
| const idx = previousIds.findIndex((x) => x === chunkId); | |||
| const nextIds = [...previousIds]; | |||
| if (checked && idx === -1) { | |||
| nextIds.push(chunkId); | |||
| } else if (!checked && idx !== -1) { | |||
| nextIds.splice(idx, 1); | |||
| } | |||
| return nextIds; | |||
| }); | |||
| }, | |||
| [], | |||
| ); | |||
| useEffect(() => { | |||
| getChunkList(); | |||
| }, [documentId, available_int, pagination]); | |||
| return () => { | |||
| dispatch({ | |||
| type: 'chunkModel/resetFilter', // TODO: need to reset state uniformly | |||
| }); | |||
| }; | |||
| }, [documentId]); | |||
| const debounceChange = debounce(getChunkList, 300); | |||
| const debounceCallback = useCallback( | |||
| @@ -117,17 +134,20 @@ const Chunk = () => { | |||
| const handleInputChange = ( | |||
| e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, | |||
| ) => { | |||
| setSelectedChunkIds([]); | |||
| const value = e.target.value; | |||
| SetKeywords(value); | |||
| debounceCallback(value); | |||
| }; | |||
| const handleSelectChange = (value: number) => { | |||
| setAvailableInt(value); | |||
| }; | |||
| return ( | |||
| <> | |||
| <div className={styles.chunkPage}> | |||
| <ChunkToolBar></ChunkToolBar> | |||
| <ChunkToolBar | |||
| getChunkList={getChunkList} | |||
| selectAllChunk={selectAllChunk} | |||
| checked={selectedChunkIds.length === data.length} | |||
| ></ChunkToolBar> | |||
| <div className={styles.filter}> | |||
| <div> | |||
| <Input | |||
| @@ -137,28 +157,6 @@ const Chunk = () => { | |||
| 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={() => { | |||
| @@ -171,86 +169,16 @@ const Chunk = () => { | |||
| </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); | |||
| }} | |||
| > | |||
| <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> | |||
| </Card> | |||
| </Col> | |||
| ); | |||
| })} | |||
| </Row> | |||
| <Space direction="vertical" size={'middle'}> | |||
| {data.map((item) => ( | |||
| <ChunkCard | |||
| item={item} | |||
| key={item.chunk_id} | |||
| checked={selectedChunkIds.some((x) => x === item.chunk_id)} | |||
| handleCheckboxClick={handleSingleCheckboxClick} | |||
| ></ChunkCard> | |||
| ))} | |||
| </Space> | |||
| </Spin> | |||
| </div> | |||
| <div className={styles.pageFooter}> | |||
| @@ -259,10 +187,10 @@ const Chunk = () => { | |||
| showLessItems | |||
| showQuickJumper | |||
| showSizeChanger | |||
| onChange={onShowSizeChange} | |||
| defaultPageSize={30} | |||
| pageSizeOptions={[30, 60, 90]} | |||
| defaultCurrent={pagination.page} | |||
| onChange={onPaginationChange} | |||
| defaultPageSize={10} | |||
| pageSizeOptions={[10, 30, 60, 90]} | |||
| defaultCurrent={pagination.current} | |||
| total={total} | |||
| /> | |||
| </div> | |||
| @@ -1,13 +1,19 @@ | |||
| import { BaseState } from '@/interfaces/common'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import kbService from '@/services/kbService'; | |||
| import { message } from 'antd'; | |||
| // import { delay } from '@/utils/storeUtil'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface ChunkModelState { | |||
| export interface ChunkModelState extends BaseState { | |||
| data: any[]; | |||
| total: number; | |||
| isShowCreateModal: boolean; | |||
| chunk_id: string; | |||
| doc_id: string; | |||
| chunkInfo: any; | |||
| documentInfo: Partial<IKnowledgeFile>; | |||
| available?: number; | |||
| } | |||
| const model: DvaModel<ChunkModelState> = { | |||
| @@ -19,6 +25,13 @@ const model: DvaModel<ChunkModelState> = { | |||
| chunk_id: '', | |||
| doc_id: '', | |||
| chunkInfo: {}, | |||
| documentInfo: {}, | |||
| pagination: { | |||
| current: 1, | |||
| pageSize: 10, | |||
| }, | |||
| searchString: '', | |||
| available: undefined, // set to undefined to select all | |||
| }, | |||
| reducers: { | |||
| updateState(state, { payload }) { | |||
| @@ -27,33 +40,56 @@ const model: DvaModel<ChunkModelState> = { | |||
| ...payload, | |||
| }; | |||
| }, | |||
| setAvailable(state, { payload }) { | |||
| return { ...state, available: payload }; | |||
| }, | |||
| setSearchString(state, { payload }) { | |||
| return { ...state, searchString: payload }; | |||
| }, | |||
| setPagination(state, { payload }) { | |||
| return { ...state, pagination: { ...state.pagination, ...payload } }; | |||
| }, | |||
| resetFilter(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| pagination: { | |||
| current: 1, | |||
| pageSize: 10, | |||
| }, | |||
| searchString: '', | |||
| available: undefined, | |||
| }; | |||
| }, | |||
| }, | |||
| // subscriptions: { | |||
| // setup({ dispatch, history }) { | |||
| // history.listen(location => { | |||
| // console.log(location) | |||
| // }); | |||
| // } | |||
| // }, | |||
| effects: { | |||
| *chunk_list({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.chunk_list, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| *chunk_list({ payload = {} }, { call, put, select }) { | |||
| const { available, searchString, pagination }: ChunkModelState = | |||
| yield select((state: any) => state.chunkModel); | |||
| const { data } = yield call(kbService.chunk_list, { | |||
| ...payload, | |||
| available_int: available, | |||
| keywords: searchString, | |||
| page: pagination.current, | |||
| size: pagination.pageSize, | |||
| }); | |||
| const { retcode, data: res } = data; | |||
| if (retcode === 0) { | |||
| console.log(res); | |||
| yield put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| data: res.chunks, | |||
| total: res.total, | |||
| documentInfo: res.doc, | |||
| }, | |||
| }); | |||
| } | |||
| }, | |||
| *switch_chunk({ payload = {} }, { call, put }) { | |||
| const { data, response } = yield call(kbService.switch_chunk, payload); | |||
| const { retcode, data: res, retmsg } = data; | |||
| const { data } = yield call(kbService.switch_chunk, payload); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success('Modified successfully !'); | |||
| } | |||
| return retcode; | |||
| }, | |||
| *rm_chunk({ payload = {} }, { call, put }) { | |||
| @@ -16,7 +16,6 @@ export interface KFModelState extends BaseState { | |||
| data: IKnowledgeFile[]; | |||
| total: number; | |||
| currentRecord: Nullable<IKnowledgeFile>; | |||
| searchString: string; | |||
| } | |||
| const model: DvaModel<KFModelState> = { | |||
| @@ -9,7 +9,6 @@ import { | |||
| UserOutlined, | |||
| } from '@ant-design/icons'; | |||
| import { Avatar, Card, Dropdown, MenuProps, Space } from 'antd'; | |||
| import { MouseEvent } from 'react'; | |||
| import { useDispatch, useNavigate } from 'umi'; | |||
| import showDeleteConfirm from '@/components/deleting-confirm'; | |||
| @@ -23,6 +22,15 @@ const KnowledgeCard = ({ item }: IProps) => { | |||
| const navigate = useNavigate(); | |||
| const dispatch = useDispatch(); | |||
| const removeKnowledge = () => { | |||
| return dispatch({ | |||
| type: 'knowledgeModel/rmKb', | |||
| payload: { | |||
| kb_id: item.id, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleDelete = () => { | |||
| showDeleteConfirm({ onOk: removeKnowledge }); | |||
| }; | |||
| @@ -47,16 +55,7 @@ const KnowledgeCard = ({ item }: IProps) => { | |||
| } | |||
| }; | |||
| const removeKnowledge = () => { | |||
| return dispatch({ | |||
| type: 'knowledgeModel/rmKb', | |||
| payload: { | |||
| kb_id: item.id, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleCardClick = (e: MouseEvent<HTMLElement>) => { | |||
| const handleCardClick = () => { | |||
| navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${item.id}`); | |||
| }; | |||
| @@ -7,3 +7,8 @@ export const getOneNamespaceEffectsLoading = ( | |||
| (effectName) => effects[`${namespace}/${effectName}`], | |||
| ); | |||
| }; | |||
| export const delay = (ms: number) => | |||
| new Promise((resolve) => { | |||
| setTimeout(resolve, ms); | |||
| }); | |||