diff --git a/web/src/assets/svg/cancel.svg b/web/src/assets/svg/cancel.svg new file mode 100644 index 000000000..97d8f72f8 --- /dev/null +++ b/web/src/assets/svg/cancel.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/web/src/assets/svg/delete.svg b/web/src/assets/svg/delete.svg new file mode 100644 index 000000000..1eef9110b --- /dev/null +++ b/web/src/assets/svg/delete.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/web/src/assets/svg/disable.svg b/web/src/assets/svg/disable.svg new file mode 100644 index 000000000..58fca0786 --- /dev/null +++ b/web/src/assets/svg/disable.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/web/src/assets/svg/enable.svg b/web/src/assets/svg/enable.svg new file mode 100644 index 000000000..bf1535b0d --- /dev/null +++ b/web/src/assets/svg/enable.svg @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/web/src/assets/svg/run.svg b/web/src/assets/svg/run.svg index e33c897cc..c3dd69761 100644 --- a/web/src/assets/svg/run.svg +++ b/web/src/assets/svg/run.svg @@ -1,15 +1,11 @@ - - - - - - - - - - + + + + \ No newline at end of file diff --git a/web/src/hooks/documentHooks.ts b/web/src/hooks/documentHooks.ts index a0b00852e..0f6b82828 100644 --- a/web/src/hooks/documentHooks.ts +++ b/web/src/hooks/documentHooks.ts @@ -155,23 +155,26 @@ export const useSetDocumentParser = () => { return setDocumentParser; }; -export const useRemoveDocument = (documentId: string) => { +export const useRemoveDocument = () => { const dispatch = useDispatch(); const { knowledgeId } = useGetKnowledgeSearchParams(); - const removeDocument = useCallback(() => { - try { - return dispatch({ - type: 'kFModel/document_rm', - payload: { - doc_id: documentId, - kb_id: knowledgeId, - }, - }); - } catch (errorInfo) { - console.log('Failed:', errorInfo); - } - }, [dispatch, knowledgeId, documentId]); + const removeDocument = useCallback( + (documentId: string) => { + try { + return dispatch({ + type: 'kFModel/document_rm', + payload: { + doc_id: documentId, + kb_id: knowledgeId, + }, + }); + } catch (errorInfo) { + console.log('Failed:', errorInfo); + } + }, + [dispatch, knowledgeId], + ); return removeDocument; }; @@ -204,11 +207,11 @@ export const useRunDocument = () => { const dispatch = useDispatch(); const runDocumentByIds = useCallback( - (ids: string[]) => { + (payload: any) => { try { return dispatch({ type: 'kFModel/document_run', - payload: { doc_ids: ids, run: 1 }, + payload, }); } catch (errorInfo) { console.log('Failed:', errorInfo); diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 29c91160d..33fb8d9cc 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -75,7 +75,8 @@ export default { chunkNumber: 'Chunk Number', uploadDate: 'Upload Date', chunkMethod: 'Chunk Method', - enabled: 'Enabled', + enabled: 'Enable', + disabled: 'Disable', action: 'Action', parsingStatus: 'Parsing Status', processBeginAt: 'Process Begin At', @@ -102,6 +103,7 @@ export default { view: 'View', filesSelected: 'Files Selected', upload: 'Upload', + run: 'Run', runningStatus0: 'UNSTART', runningStatus1: 'Parsing', runningStatus2: 'CANCEL', @@ -130,6 +132,8 @@ export default { uploadDescription: 'Support for a single or bulk upload. Strictly prohibited from uploading company data or other banned files.', chunk: 'Chunk', + bulk: 'Bulk', + cancel: 'Cancel', }, knowledgeConfiguration: { titleDescription: diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index af162fe9f..70a3b42c7 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -75,6 +75,7 @@ export default { uploadDate: '上传日期', chunkMethod: '解析方法', enabled: '启用', + disabled: '禁用', action: '动作', parsingStatus: '解析状态', processBeginAt: '流程开始于', @@ -100,6 +101,7 @@ export default { view: '看法', filesSelected: '选定的文件', upload: '上传', + run: '启动', runningStatus0: '未启动', runningStatus1: '解析中', runningStatus2: '取消', @@ -127,6 +129,8 @@ export default { uploadDescription: '支持单次或批量上传。 严禁上传公司数据或其他违禁文件。', chunk: '解析块', + bulk: '批量', + cancel: '取消', }, knowledgeConfiguration: { titleDescription: '在这里更新您的知识库详细信息,尤其是解析方法。', diff --git a/web/src/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file/index.tsx b/web/src/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file/index.tsx index 263457a75..b7025e1cf 100644 --- a/web/src/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-dataset/knowledge-upload-file/index.tsx @@ -219,7 +219,7 @@ const KnowledgeUploadFile = () => { const runSelectedDocument = () => { const ids = fileListRef.current.map((x) => x.response.id); - runDocumentByIds(ids); + runDocumentByIds({ doc_ids: ids, run: 1 }); }; const handleNextClick = () => { diff --git a/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx b/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx new file mode 100644 index 000000000..f6fb5bef2 --- /dev/null +++ b/web/src/pages/add-knowledge/components/knowledge-file/document-toolbar.tsx @@ -0,0 +1,222 @@ +import { ReactComponent as CancelIcon } from '@/assets/svg/cancel.svg'; +import { ReactComponent as DeleteIcon } from '@/assets/svg/delete.svg'; +import { ReactComponent as DisableIcon } from '@/assets/svg/disable.svg'; +import { ReactComponent as EnableIcon } from '@/assets/svg/enable.svg'; +import { ReactComponent as RunIcon } from '@/assets/svg/run.svg'; +import { useShowDeleteConfirm, useTranslate } from '@/hooks/commonHooks'; +import { + useRemoveDocument, + useRunDocument, + useSetDocumentStatus, +} from '@/hooks/documentHooks'; +import { useGetKnowledgeSearchParams } from '@/hooks/routeHook'; +import { + DownOutlined, + FileOutlined, + FileTextOutlined, + PlusOutlined, + SearchOutlined, +} from '@ant-design/icons'; +import { Button, Dropdown, Flex, Input, MenuProps, Space } from 'antd'; +import { useCallback, useMemo } from 'react'; +import { + useFetchDocumentListOnMount, + useGetPagination, + useHandleSearchChange, + useNavigateToOtherPage, +} from './hooks'; +import styles from './index.less'; + +interface IProps { + selectedRowKeys: string[]; + showCreateModal(): void; +} + +const DocumentToolbar = ({ selectedRowKeys, showCreateModal }: IProps) => { + const { t } = useTranslate('knowledgeDetails'); + const { fetchDocumentList } = useFetchDocumentListOnMount(); + const { setPagination, searchString } = useGetPagination(fetchDocumentList); + const { handleInputChange } = useHandleSearchChange(setPagination); + const removeDocument = useRemoveDocument(); + const showDeleteConfirm = useShowDeleteConfirm(); + const { linkToUploadPage } = useNavigateToOtherPage(); + const runDocumentByIds = useRunDocument(); + const { knowledgeId } = useGetKnowledgeSearchParams(); + const changeStatus = useSetDocumentStatus(); + + const actionItems: MenuProps['items'] = useMemo(() => { + return [ + { + key: '1', + onClick: linkToUploadPage, + label: ( +
+ +
+ ), + }, + { type: 'divider' }, + { + key: '2', + onClick: showCreateModal, + label: ( +
+ +
+ ), + // disabled: true, + }, + ]; + }, [linkToUploadPage, showCreateModal, t]); + + const handleDelete = useCallback(() => { + showDeleteConfirm({ + onOk: () => { + selectedRowKeys.forEach((id) => { + removeDocument(id); + }); + }, + }); + }, [removeDocument, showDeleteConfirm, selectedRowKeys]); + + const runDocument = useCallback( + (run: number) => { + runDocumentByIds({ + doc_ids: selectedRowKeys, + run, + knowledgeBaseId: knowledgeId, + }); + }, + [runDocumentByIds, selectedRowKeys, knowledgeId], + ); + + const handleRunClick = useCallback(() => { + runDocument(1); + }, [runDocument]); + + const handleCancelClick = useCallback(() => { + runDocument(2); + }, [runDocument]); + + const onChangeStatus = useCallback( + (enabled: boolean) => { + selectedRowKeys.forEach((id) => { + changeStatus(enabled, id); + }); + }, + [selectedRowKeys, changeStatus], + ); + + const handleEnableClick = useCallback(() => { + onChangeStatus(true); + }, [onChangeStatus]); + + const handleDisableClick = useCallback(() => { + onChangeStatus(false); + }, [onChangeStatus]); + + const disabled = selectedRowKeys.length === 0; + + const items: MenuProps['items'] = useMemo(() => { + return [ + { + key: '0', + onClick: handleEnableClick, + label: ( + + + {t('enabled')} + + ), + }, + { + key: '1', + onClick: handleDisableClick, + label: ( + + + {t('disabled')} + + ), + }, + { type: 'divider' }, + { + key: '2', + onClick: handleRunClick, + label: ( + + + {t('run')} + + ), + }, + { + key: '3', + onClick: handleCancelClick, + label: ( + + + {t('cancel')} + + ), + }, + { type: 'divider' }, + { + key: '4', + onClick: handleDelete, + label: ( + + + + + {t('delete', { keyPrefix: 'common' })} + + ), + }, + ]; + }, [handleDelete, handleRunClick, handleCancelClick, t]); + + return ( +
+ + + + + } + /> + + + + + +
+ ); +}; + +export default DocumentToolbar; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts b/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts index 1bf98ca29..4091bfd99 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts +++ b/web/src/pages/add-knowledge/components/knowledge-file/hooks.ts @@ -1,4 +1,4 @@ -import { useSetModalState } from '@/hooks/commonHooks'; +import { useSetModalState, useTranslate } from '@/hooks/commonHooks'; import { useCreateDocument, useFetchDocumentList, @@ -11,7 +11,7 @@ import { useFetchTenantInfo } from '@/hooks/userSettingHook'; import { Pagination } from '@/interfaces/common'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { PaginationProps } from 'antd'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch, useNavigate, useSelector } from 'umi'; import { KnowledgeRouteKey } from './constant'; @@ -43,6 +43,7 @@ export const useFetchDocumentListOnMount = () => { export const useGetPagination = (fetchDocumentList: () => void) => { const dispatch = useDispatch(); const kFModel = useSelector((state: any) => state.kFModel); + const { t } = useTranslate('common'); const setPagination = useCallback( (pageNumber = 1, pageSize?: number) => { @@ -77,8 +78,9 @@ export const useGetPagination = (fetchDocumentList: () => void) => { pageSize: kFModel.pagination.pageSize, pageSizeOptions: [1, 2, 10, 20, 50, 100], onChange: onPageChange, + showTotal: (total) => `${t('total')} ${total}`, }; - }, [kFModel, onPageChange]); + }, [kFModel, onPageChange, t]); return { pagination, @@ -227,3 +229,16 @@ export const useChangeDocumentParser = (documentId: string) => { showChangeParserModal, }; }; + +export const useGetRowSelection = () => { + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + + const rowSelection = { + selectedRowKeys, + onChange: (newSelectedRowKeys: React.Key[]) => { + setSelectedRowKeys(newSelectedRowKeys); + }, + }; + + return rowSelection; +}; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/index.less b/web/src/pages/add-knowledge/components/knowledge-file/index.less index d78737b36..fa7139c05 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-file/index.less @@ -8,15 +8,13 @@ display: flex; margin: 10px 0; justify-content: space-between; - padding: 24px 20px; + padding: 24px 0; + align-items: center; +} - // .search { - // flex: 1; - // } - - // .operate { - // width: 200px; - // } +.deleteIconWrapper { + width: 22px; + text-align: center; } .img { 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 096b22de4..765d67bc8 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx @@ -4,36 +4,21 @@ import { useSelectDocumentList, useSetDocumentStatus, } from '@/hooks/documentHooks'; +import { useSetSelectedRecord } from '@/hooks/logicHooks'; import { useSelectParserList } from '@/hooks/userSettingHook'; import { IKnowledgeFile } from '@/interfaces/database/knowledge'; import { getExtension } from '@/utils/documentUtils'; -import { - FileOutlined, - FileTextOutlined, - PlusOutlined, - SearchOutlined, -} from '@ant-design/icons'; -import type { MenuProps } from 'antd'; -import { - Button, - Divider, - Dropdown, - Flex, - Input, - Space, - Switch, - Table, - Tag, -} from 'antd'; +import { Divider, Flex, Switch, Table } from 'antd'; import type { ColumnsType } from 'antd/es/table'; -import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import CreateFileModal from './create-file-modal'; +import DocumentToolbar from './document-toolbar'; import { useChangeDocumentParser, useCreateEmptyDocument, useFetchDocumentListOnMount, useGetPagination, - useHandleSearchChange, + useGetRowSelection, useNavigateToOtherPage, useRenameDocument, } from './hooks'; @@ -41,20 +26,15 @@ import ParsingActionCell from './parsing-action-cell'; import ParsingStatusCell from './parsing-status-cell'; import RenameModal from './rename-modal'; -import { useSetSelectedRecord } from '@/hooks/logicHooks'; -import { useTranslation } from 'react-i18next'; import styles from './index.less'; const KnowledgeFile = () => { const data = useSelectDocumentList(); const { fetchDocumentList } = useFetchDocumentListOnMount(); const parserList = useSelectParserList(); - const { pagination, setPagination, total, searchString } = - useGetPagination(fetchDocumentList); + const { pagination } = useGetPagination(fetchDocumentList); const onChangeStatus = useSetDocumentStatus(); - const { linkToUploadPage, toChunk } = useNavigateToOtherPage(); - - const { handleInputChange } = useHandleSearchChange(setPagination); + const { toChunk } = useNavigateToOtherPage(); const { currentRecord, setRecord } = useSetSelectedRecord(); const { renameLoading, @@ -81,38 +61,7 @@ const KnowledgeFile = () => { keyPrefix: 'knowledgeDetails', }); - const actionItems: MenuProps['items'] = useMemo(() => { - return [ - { - key: '1', - onClick: linkToUploadPage, - label: ( -
- -
- ), - }, - { type: 'divider' }, - { - key: '2', - onClick: showCreateModal, - label: ( -
- -
- ), - // disabled: true, - }, - ]; - }, [linkToUploadPage, showCreateModal, t]); + const rowSelection = useGetRowSelection(); const columns: ColumnsType = [ { @@ -161,7 +110,7 @@ const KnowledgeFile = () => { render: (_, { status, id }) => ( <> { onChangeStatus(e, id); }} @@ -201,36 +150,17 @@ const KnowledgeFile = () => {

{t('dataset')}

{t('datasetDescription')}

-
- -

{t('total', { keyPrefix: 'common' })}

- - {total} {t('files')} - -
- - } - /> - - - - - -
+ { if (!isRunning) { - showDeleteConfirm({ onOk: removeDocument }); + showDeleteConfirm({ onOk: () => removeDocument(documentId) }); } }; diff --git a/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.less b/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.less index 25ccfaaa2..c55127f26 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.less @@ -18,8 +18,7 @@ .operationIcon { text-align: center; - margin-right: 20%; - width: 20px; + display: flex; &:hover { cursor: pointer; } diff --git a/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx b/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx index d5ca83e88..7e2d102a9 100644 --- a/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-file/parsing-status-cell/index.tsx @@ -1,19 +1,19 @@ +import { ReactComponent as CancelIcon } from '@/assets/svg/cancel.svg'; import { ReactComponent as RefreshIcon } from '@/assets/svg/refresh.svg'; import { ReactComponent as RunIcon } from '@/assets/svg/run.svg'; import { useTranslate } from '@/hooks/commonHooks'; +import { useRunDocument } from '@/hooks/documentHooks'; import { IKnowledgeFile } from '@/interfaces/database/knowledge'; -import { CloseCircleOutlined } from '@ant-design/icons'; import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd'; import { useTranslation } from 'react-i18next'; import reactStringReplace from 'react-string-replace'; -import { useDispatch } from 'umi'; import { RunningStatus, RunningStatusMap } from '../constant'; import { isParserRunning } from '../utils'; import styles from './index.less'; const iconMap = { [RunningStatus.UNSTART]: RunIcon, - [RunningStatus.RUNNING]: CloseCircleOutlined, + [RunningStatus.RUNNING]: CancelIcon, [RunningStatus.CANCEL]: RefreshIcon, [RunningStatus.DONE]: RefreshIcon, [RunningStatus.FAIL]: RefreshIcon, @@ -78,10 +78,10 @@ const PopoverContent = ({ record }: IProps) => { }; export const ParsingStatusCell = ({ record }: IProps) => { - const dispatch = useDispatch(); const text = record.run; const runningStatus = RunningStatusMap[text]; const { t } = useTranslation(); + const runDocumentByIds = useRunDocument(); const isRunning = isParserRunning(text); @@ -90,18 +90,15 @@ export const ParsingStatusCell = ({ record }: IProps) => { const label = t(`knowledgeDetails.runningStatus${text}`); const handleOperationIconClick = () => { - dispatch({ - type: 'kFModel/document_run', - payload: { - doc_ids: [record.id], - run: isRunning ? 2 : 1, - knowledgeBaseId: record.kb_id, - }, + runDocumentByIds({ + doc_ids: [record.id], + run: isRunning ? 2 : 1, + knowledgeBaseId: record.kb_id, }); }; return ( - + }> {isRunning ? (