diff --git a/web/.eslintrc.js b/web/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..8f9843ed625925d57b8c44fe2cfe162102beed6e --- /dev/null +++ b/web/.eslintrc.js @@ -0,0 +1,5 @@ +// .eslintrc.js +module.exports = { + // Umi 项目 + extends: [require.resolve('umi/eslint'), 'plugin:react-hooks/recommended'], +}; diff --git a/web/package-lock.json b/web/package-lock.json index 6906fc24cb3e900a821d6158226ffcd663d751b1..194342df9a88b95a4040aea3285cbbefc789e262 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -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", diff --git a/web/package.json b/web/package.json index 41b054d5742fc7f1bbeced898b17c138b8e98afa..9f3f056bd37cbd48132382a6d02777355cf17784 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/src/app.tsx b/web/src/app.tsx index 15473641ceb02b364fbe63f6cddd560b9d417a69..a67e507ec44658a243f39f10a435abc2dd98b451 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -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); diff --git a/web/src/interfaces/common.ts b/web/src/interfaces/common.ts index f99511fa3f661dfbc45d34db40b00cc2cbb5668c..518d077c04a07163e1ca7e5aed63873f3f4de644 100644 --- a/web/src/interfaces/common.ts +++ b/web/src/interfaces/common.ts @@ -5,4 +5,5 @@ export interface Pagination { export interface BaseState { pagination: Pagination; + searchString: string; } diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index 24cebae515c393e3a902c8010b7aa04dcda11c34..a98a04bec90593f1ca35e9fe9155c53fdf87cbeb 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -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[]; +} diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-card/index.less b/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-card/index.less new file mode 100644 index 0000000000000000000000000000000000000000..876e426394c6687a9b44d31b9643aac560424261 --- /dev/null +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-card/index.less @@ -0,0 +1,8 @@ +.image { + width: 100px !important; + min-width: 100px; +} + +.imagePreview { + width: 600px; +} diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-card/index.tsx b/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-card/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d4d169a5872fb2d1445f431847bb42e9408cb6cd --- /dev/null +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-card/index.tsx @@ -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; diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-toolbar/index.tsx b/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-toolbar/index.tsx index d812c8e3e3f5a91318ffdef3a7dafaeb9774d699..8200f71d6ab2198287e6389136e9d312b604f32f 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-toolbar/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/components/chunk-toolbar/index.tsx @@ -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> diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/index.less b/web/src/pages/add-knowledge/components/knowledge-chunk/index.less index f4f42fb995d9e2d53c2b456ba6609af666244845..76336464b95ffd2b6776702eb3b5fe2ecd50bbd5 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/index.less @@ -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; + } -} \ No newline at end of file + cursor: pointer; +} 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 d303a710f4918ae0df10dd06f3a80faa93cea53c..236ba184979f90c363d3cd9262c0706d8001a85c 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/index.tsx @@ -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> 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 2ebba51716703d9234c9a11be1821c91a886cdae..2df2af8814bba11b88ccdb9e9c1c976f947a1e63 100644 --- a/web/src/pages/add-knowledge/components/knowledge-chunk/model.ts +++ b/web/src/pages/add-knowledge/components/knowledge-chunk/model.ts @@ -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 }) { 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 875665b718e1b8f168f0584477e925166ef5e103..1b673355efd35f1c031ea7ad6bf6ba941c19b28d 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/model.ts +++ b/web/src/pages/add-knowledge/components/knowledge-file/model.ts @@ -16,7 +16,6 @@ export interface KFModelState extends BaseState { data: IKnowledgeFile[]; total: number; currentRecord: Nullable<IKnowledgeFile>; - searchString: string; } const model: DvaModel<KFModelState> = { diff --git a/web/src/pages/knowledge/knowledge-card/index.tsx b/web/src/pages/knowledge/knowledge-card/index.tsx index 07d1616f6bbf5bd5d8f623cb5f3c8017fd5990f7..199b7638656dbcba74ea2ade1353bc5d7bf458c1 100644 --- a/web/src/pages/knowledge/knowledge-card/index.tsx +++ b/web/src/pages/knowledge/knowledge-card/index.tsx @@ -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}`); }; diff --git a/web/src/utils/storeUtil.ts b/web/src/utils/storeUtil.ts index 4437d25fcf03d65956a2b763eedb2282e9b438af..dfa25ab9c9f35163750baa1c67487849a4152558 100644 --- a/web/src/utils/storeUtil.ts +++ b/web/src/utils/storeUtil.ts @@ -7,3 +7,8 @@ export const getOneNamespaceEffectsLoading = ( (effectName) => effects[`${namespace}/${effectName}`], ); }; + +export const delay = (ms: number) => + new Promise((resolve) => { + setTimeout(resolve, ms); + });