### What problem does this PR solve? feat: add file icon to table of FileManager #345 fix: modify datasetDescription ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.3.1
| <svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||||
| <path | |||||
| d="M1.32202e-08 2.54731L21.5 2.54731C22.8807 2.54731 24 3.4977 24 4.67006L24 15.2838C24 16.4562 22.8807 17.4066 21.5 17.4066L12 17.4066L2.5 17.4066C1.11929 17.4066 8.54054e-08 16.4562 7.9321e-08 15.2838L1.32202e-08 2.54731Z" | |||||
| fill="#FBBC1A" /> | |||||
| <path | |||||
| d="M2.97454e-08 5.73144L7.49143e-08 14.4347C8.09987e-08 15.6071 1.11929 16.5575 2.5 16.5575L21.5 16.5575C22.8807 16.5575 24 15.6071 24 14.4347L24 5.51916C24 4.3468 22.8807 3.39641 21.5 3.39641L11 3.39641L11 4.45779C11 5.16121 10.3284 5.73144 9.5 5.73144L2.97454e-08 5.73144Z" | |||||
| fill="url(#paint0_linear_2323_8307)" /> | |||||
| <path | |||||
| d="M8.81345e-09 1.6982C3.94591e-09 0.760312 0.89543 -4.64716e-09 2 -1.03797e-08L9 -4.67088e-08C10.1046 -5.24413e-08 11 0.760312 11 1.6982L11 2.54731L1.32202e-08 2.54731L8.81345e-09 1.6982Z" | |||||
| fill="#FBBC1A" /> | |||||
| <defs> | |||||
| <linearGradient id="paint0_linear_2323_8307" x1="0" y1="0" x2="28.8004" y2="20.3231" | |||||
| gradientUnits="userSpaceOnUse"> | |||||
| <stop stop-color="#FFE69C" /> | |||||
| <stop offset="1" stop-color="#FFC937" /> | |||||
| </linearGradient> | |||||
| </defs> | |||||
| </svg> |
| import isObject from 'lodash/isObject'; | |||||
| import { DvaModel } from 'umi'; | |||||
| import { BaseState } from './interfaces/common'; | |||||
| type State = Record<string, any>; | |||||
| type DvaModelKey<T> = keyof DvaModel<T>; | |||||
| export const modelExtend = <T>( | |||||
| baseModel: Partial<DvaModel<any>>, | |||||
| extendModel: DvaModel<any>, | |||||
| ): DvaModel<T> => { | |||||
| return Object.keys(extendModel).reduce<DvaModel<T>>((pre, cur) => { | |||||
| const baseValue = baseModel[cur as DvaModelKey<State>]; | |||||
| const value = extendModel[cur as DvaModelKey<State>]; | |||||
| if (isObject(value) && isObject(baseValue) && typeof value !== 'string') { | |||||
| const key = cur as Exclude<DvaModelKey<State>, 'namespace'>; | |||||
| pre[key] = { | |||||
| ...baseValue, | |||||
| ...value, | |||||
| } as any; | |||||
| } else { | |||||
| pre[cur as DvaModelKey<State>] = value as any; | |||||
| } | |||||
| return pre; | |||||
| }, {} as DvaModel<T>); | |||||
| }; | |||||
| export const paginationModel: Partial<DvaModel<BaseState>> = { | |||||
| state: { | |||||
| searchString: '', | |||||
| pagination: { | |||||
| total: 0, | |||||
| current: 1, | |||||
| pageSize: 10, | |||||
| }, | |||||
| }, | |||||
| reducers: { | |||||
| setSearchString(state, { payload }) { | |||||
| return { ...state, searchString: payload }; | |||||
| }, | |||||
| setPagination(state, { payload }) { | |||||
| return { ...state, pagination: { ...state.pagination, ...payload } }; | |||||
| }, | |||||
| }, | |||||
| }; |
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const uploadFile = useCallback( | const uploadFile = useCallback( | ||||
| (file: UploadFile, parentId: string, path: string) => { | |||||
| (fileList: UploadFile[], parentId: string) => { | |||||
| try { | try { | ||||
| return dispatch<any>({ | return dispatch<any>({ | ||||
| type: 'fileManager/uploadFile', | type: 'fileManager/uploadFile', | ||||
| payload: { | payload: { | ||||
| file, | |||||
| file: fileList, | |||||
| parentId, | parentId, | ||||
| path, | |||||
| path: fileList.map((file) => (file as any).webkitRelativePath), | |||||
| }, | }, | ||||
| }); | }); | ||||
| } catch (errorInfo) { | } catch (errorInfo) { |
| export const useFetchKnowledgeList = ( | export const useFetchKnowledgeList = ( | ||||
| shouldFilterListWithoutDocument: boolean = false, | shouldFilterListWithoutDocument: boolean = false, | ||||
| ): { list: IKnowledge[]; loading: boolean } => { | |||||
| ) => { | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']); | const loading = useOneNamespaceEffectsLoading('knowledgeModel', ['getList']); | ||||
| const knowledgeModel = useSelector((state: any) => state.knowledgeModel); | const knowledgeModel = useSelector((state: any) => state.knowledgeModel); | ||||
| const { data = [] } = knowledgeModel; | const { data = [] } = knowledgeModel; | ||||
| const list = useMemo(() => { | |||||
| const list: IKnowledge[] = useMemo(() => { | |||||
| return shouldFilterListWithoutDocument | return shouldFilterListWithoutDocument | ||||
| ? data.filter((x: IKnowledge) => x.chunk_num > 0) | ? data.filter((x: IKnowledge) => x.chunk_num > 0) | ||||
| : data; | : data; | ||||
| fetchList(); | fetchList(); | ||||
| }, [fetchList]); | }, [fetchList]); | ||||
| return { list, loading }; | |||||
| return { list, loading, fetchList }; | |||||
| }; | }; | ||||
| export const useSelectFileThumbnails = () => { | export const useSelectFileThumbnails = () => { |
| import { LanguageTranslationMap } from '@/constants/common'; | import { LanguageTranslationMap } from '@/constants/common'; | ||||
| import { Pagination } from '@/interfaces/common'; | |||||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | ||||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | ||||
| import { useCallback, useState } from 'react'; | |||||
| import { PaginationProps } from 'antd'; | |||||
| import { useCallback, useMemo, useState } from 'react'; | |||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { useSetModalState } from './commonHooks'; | |||||
| import { useDispatch } from 'umi'; | |||||
| import { useSetModalState, useTranslate } from './commonHooks'; | |||||
| import { useSetDocumentParser } from './documentHooks'; | import { useSetDocumentParser } from './documentHooks'; | ||||
| import { useOneNamespaceEffectsLoading } from './storeHooks'; | import { useOneNamespaceEffectsLoading } from './storeHooks'; | ||||
| import { useSaveSetting } from './userSettingHook'; | import { useSaveSetting } from './userSettingHook'; | ||||
| return changeLanguage; | return changeLanguage; | ||||
| }; | }; | ||||
| export const useGetPagination = ( | |||||
| total: number, | |||||
| page: number, | |||||
| pageSize: number, | |||||
| onPageChange: PaginationProps['onChange'], | |||||
| ) => { | |||||
| const { t } = useTranslate('common'); | |||||
| const pagination: PaginationProps = useMemo(() => { | |||||
| return { | |||||
| showQuickJumper: true, | |||||
| total, | |||||
| showSizeChanger: true, | |||||
| current: page, | |||||
| pageSize: pageSize, | |||||
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |||||
| onChange: onPageChange, | |||||
| showTotal: (total) => `${t('total')} ${total}`, | |||||
| }; | |||||
| }, [t, onPageChange, page, pageSize, total]); | |||||
| return { | |||||
| pagination, | |||||
| }; | |||||
| }; | |||||
| export const useSetPagination = (namespace: string) => { | |||||
| const dispatch = useDispatch(); | |||||
| const setPagination = useCallback( | |||||
| (pageNumber = 1, pageSize?: number) => { | |||||
| const pagination: Pagination = { | |||||
| current: pageNumber, | |||||
| } as Pagination; | |||||
| if (pageSize) { | |||||
| pagination.pageSize = pageSize; | |||||
| } | |||||
| dispatch({ | |||||
| type: `${namespace}/setPagination`, | |||||
| payload: pagination, | |||||
| }); | |||||
| }, | |||||
| [dispatch, namespace], | |||||
| ); | |||||
| return setPagination; | |||||
| }; |
| export interface Pagination { | export interface Pagination { | ||||
| current: number; | current: number; | ||||
| pageSize: number; | pageSize: number; | ||||
| total: number; | |||||
| } | } | ||||
| export interface BaseState { | export interface BaseState { |
| namePlaceholder: 'Please input name!', | namePlaceholder: 'Please input name!', | ||||
| doc: 'Docs', | doc: 'Docs', | ||||
| datasetDescription: | datasetDescription: | ||||
| "Hey, don't forget to adjust the chunk after adding the dataset! 😉", | |||||
| '😉 Questions and answers can only be answered after the parsing is successful.', | |||||
| addFile: 'Add file', | addFile: 'Add file', | ||||
| searchFiles: 'Search your files', | searchFiles: 'Search your files', | ||||
| localFiles: 'Local files', | localFiles: 'Local files', |
| name: '名稱', | name: '名稱', | ||||
| namePlaceholder: '請輸入名稱', | namePlaceholder: '請輸入名稱', | ||||
| doc: '文件', | doc: '文件', | ||||
| datasetDescription: '嘿,添加數據集後別忘了調整解析塊!😉', | |||||
| datasetDescription: '😉 解析成功後才能問答哦。', | |||||
| addFile: '新增文件', | addFile: '新增文件', | ||||
| searchFiles: '搜索文件', | searchFiles: '搜索文件', | ||||
| localFiles: '本地文件', | localFiles: '本地文件', |
| name: '名称', | name: '名称', | ||||
| namePlaceholder: '请输入名称', | namePlaceholder: '请输入名称', | ||||
| doc: '文档', | doc: '文档', | ||||
| datasetDescription: '嘿,添加数据集后别忘了调整解析块! 😉', | |||||
| datasetDescription: '😉 解析成功后才能问答哦。', | |||||
| addFile: '新增文件', | addFile: '新增文件', | ||||
| searchFiles: '搜索文件', | searchFiles: '搜索文件', | ||||
| localFiles: '本地文件', | localFiles: '本地文件', |
| record: IFile; | record: IFile; | ||||
| setCurrentRecord: (record: any) => void; | setCurrentRecord: (record: any) => void; | ||||
| showRenameModal: (record: IFile) => void; | showRenameModal: (record: IFile) => void; | ||||
| showConnectToKnowledgeModal: (ids: string[]) => void; | |||||
| showConnectToKnowledgeModal: (record: IFile) => void; | |||||
| setSelectedRowKeys(keys: string[]): void; | |||||
| } | } | ||||
| const ActionCell = ({ | const ActionCell = ({ | ||||
| setCurrentRecord, | setCurrentRecord, | ||||
| showRenameModal, | showRenameModal, | ||||
| showConnectToKnowledgeModal, | showConnectToKnowledgeModal, | ||||
| setSelectedRowKeys, | |||||
| }: IProps) => { | }: IProps) => { | ||||
| const documentId = record.id; | const documentId = record.id; | ||||
| const beingUsed = false; | const beingUsed = false; | ||||
| const { t } = useTranslate('knowledgeDetails'); | const { t } = useTranslate('knowledgeDetails'); | ||||
| const { handleRemoveFile } = useHandleDeleteFile([documentId]); | |||||
| const { handleRemoveFile } = useHandleDeleteFile( | |||||
| [documentId], | |||||
| setSelectedRowKeys, | |||||
| ); | |||||
| const onDownloadDocument = () => { | const onDownloadDocument = () => { | ||||
| downloadFile({ | downloadFile({ | ||||
| }; | }; | ||||
| const onShowConnectToKnowledgeModal = () => { | const onShowConnectToKnowledgeModal = () => { | ||||
| showConnectToKnowledgeModal([documentId]); | |||||
| showConnectToKnowledgeModal(record); | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| > | > | ||||
| <DeleteOutlined size={20} /> | <DeleteOutlined size={20} /> | ||||
| </Button> | </Button> | ||||
| <Button | |||||
| type="text" | |||||
| disabled={beingUsed} | |||||
| onClick={onDownloadDocument} | |||||
| className={styles.iconButton} | |||||
| > | |||||
| <DownloadOutlined size={20} /> | |||||
| </Button> | |||||
| {record.type !== 'folder' && ( | |||||
| <Button | |||||
| type="text" | |||||
| disabled={beingUsed} | |||||
| onClick={onDownloadDocument} | |||||
| className={styles.iconButton} | |||||
| > | |||||
| <DownloadOutlined size={20} /> | |||||
| </Button> | |||||
| )} | |||||
| </Space> | </Space> | ||||
| ); | ); | ||||
| }; | }; |
| import { useFetchKnowledgeList } from '@/hooks/knowledgeHook'; | import { useFetchKnowledgeList } from '@/hooks/knowledgeHook'; | ||||
| import { IModalProps } from '@/interfaces/common'; | import { IModalProps } from '@/interfaces/common'; | ||||
| import { Form, Modal, Select, SelectProps } from 'antd'; | import { Form, Modal, Select, SelectProps } from 'antd'; | ||||
| import { useEffect } from 'react'; | |||||
| const ConnectToKnowledgeModal = ({ | const ConnectToKnowledgeModal = ({ | ||||
| visible, | visible, | ||||
| hideModal, | hideModal, | ||||
| onOk, | onOk, | ||||
| }: IModalProps<string[]>) => { | |||||
| initialValue, | |||||
| }: IModalProps<string[]> & { initialValue: string[] }) => { | |||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const { list } = useFetchKnowledgeList(); | |||||
| const { list, fetchList } = useFetchKnowledgeList(); | |||||
| const options: SelectProps['options'] = list?.map((item) => ({ | const options: SelectProps['options'] = list?.map((item) => ({ | ||||
| label: item.name, | label: item.name, | ||||
| const handleOk = async () => { | const handleOk = async () => { | ||||
| const values = await form.getFieldsValue(); | const values = await form.getFieldsValue(); | ||||
| const knowledgeIds = values.knowledgeIds ?? []; | const knowledgeIds = values.knowledgeIds ?? []; | ||||
| if (knowledgeIds.length > 0) { | |||||
| return onOk?.(knowledgeIds); | |||||
| } | |||||
| return onOk?.(knowledgeIds); | |||||
| }; | }; | ||||
| useEffect(() => { | |||||
| if (visible) { | |||||
| form.setFieldValue('knowledgeIds', initialValue); | |||||
| fetchList(); | |||||
| } | |||||
| }, [visible, fetchList, initialValue, form]); | |||||
| return ( | return ( | ||||
| <Modal | <Modal | ||||
| title="Add to Knowledge Base" | title="Add to Knowledge Base" | ||||
| onCancel={hideModal} | onCancel={hideModal} | ||||
| > | > | ||||
| <Form form={form}> | <Form form={form}> | ||||
| <Form.Item | |||||
| name="knowledgeIds" | |||||
| noStyle | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: 'Please select your favourite colors!', | |||||
| type: 'array', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Form.Item name="knowledgeIds" noStyle> | |||||
| <Select | <Select | ||||
| mode="multiple" | mode="multiple" | ||||
| allowClear | allowClear |
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| import { | import { | ||||
| useFetchDocumentListOnMount, | useFetchDocumentListOnMount, | ||||
| useGetPagination, | |||||
| useHandleDeleteFile, | useHandleDeleteFile, | ||||
| useHandleSearchChange, | useHandleSearchChange, | ||||
| useSelectBreadcrumbItems, | useSelectBreadcrumbItems, | ||||
| selectedRowKeys: string[]; | selectedRowKeys: string[]; | ||||
| showFolderCreateModal: () => void; | showFolderCreateModal: () => void; | ||||
| showFileUploadModal: () => void; | showFileUploadModal: () => void; | ||||
| setSelectedRowKeys: (keys: string[]) => void; | |||||
| } | } | ||||
| const itemRender: BreadcrumbProps['itemRender'] = ( | const itemRender: BreadcrumbProps['itemRender'] = ( | ||||
| selectedRowKeys, | selectedRowKeys, | ||||
| showFolderCreateModal, | showFolderCreateModal, | ||||
| showFileUploadModal, | showFileUploadModal, | ||||
| setSelectedRowKeys, | |||||
| }: IProps) => { | }: IProps) => { | ||||
| const { t } = useTranslate('knowledgeDetails'); | const { t } = useTranslate('knowledgeDetails'); | ||||
| const { fetchDocumentList } = useFetchDocumentListOnMount(); | |||||
| const { setPagination, searchString } = useGetPagination(fetchDocumentList); | |||||
| const { handleInputChange } = useHandleSearchChange(setPagination); | |||||
| useFetchDocumentListOnMount(); | |||||
| const { handleInputChange, searchString } = useHandleSearchChange(); | |||||
| const breadcrumbItems = useSelectBreadcrumbItems(); | const breadcrumbItems = useSelectBreadcrumbItems(); | ||||
| const actionItems: MenuProps['items'] = useMemo(() => { | const actionItems: MenuProps['items'] = useMemo(() => { | ||||
| ]; | ]; | ||||
| }, [t, showFolderCreateModal, showFileUploadModal]); | }, [t, showFolderCreateModal, showFileUploadModal]); | ||||
| const { handleRemoveFile } = useHandleDeleteFile(selectedRowKeys); | |||||
| const { handleRemoveFile } = useHandleDeleteFile( | |||||
| selectedRowKeys, | |||||
| setSelectedRowKeys, | |||||
| ); | |||||
| const disabled = selectedRowKeys.length === 0; | const disabled = selectedRowKeys.length === 0; | ||||
| UploadProps, | UploadProps, | ||||
| } from 'antd'; | } from 'antd'; | ||||
| import { Dispatch, SetStateAction, useState } from 'react'; | import { Dispatch, SetStateAction, useState } from 'react'; | ||||
| import { useHandleUploadFile } from '../hooks'; | |||||
| const { Dragger } = Upload; | const { Dragger } = Upload; | ||||
| ); | ); | ||||
| }; | }; | ||||
| const FileUploadModal = ({ visible, hideModal }: IModalProps<any>) => { | |||||
| const FileUploadModal = ({ | |||||
| visible, | |||||
| hideModal, | |||||
| loading, | |||||
| onOk: onFileUploadOk, | |||||
| }: IModalProps<UploadFile[]>) => { | |||||
| const [value, setValue] = useState<string | number>('local'); | const [value, setValue] = useState<string | number>('local'); | ||||
| const { onFileUploadOk, fileUploadLoading } = useHandleUploadFile(); | |||||
| const [fileList, setFileList] = useState<UploadFile[]>([]); | const [fileList, setFileList] = useState<UploadFile[]>([]); | ||||
| const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]); | const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]); | ||||
| const onOk = () => { | const onOk = () => { | ||||
| onFileUploadOk([...fileList, ...directoryFileList]); | |||||
| return onFileUploadOk?.([...fileList, ...directoryFileList]); | |||||
| }; | }; | ||||
| const items: TabsProps['items'] = [ | const items: TabsProps['items'] = [ | ||||
| open={visible} | open={visible} | ||||
| onOk={onOk} | onOk={onOk} | ||||
| onCancel={hideModal} | onCancel={hideModal} | ||||
| confirmLoading={fileUploadLoading} | |||||
| confirmLoading={loading} | |||||
| > | > | ||||
| <Flex gap={'large'} vertical> | <Flex gap={'large'} vertical> | ||||
| <Segmented | <Segmented |
| import { | |||||
| useSetModalState, | |||||
| useShowDeleteConfirm, | |||||
| useTranslate, | |||||
| } from '@/hooks/commonHooks'; | |||||
| import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks'; | |||||
| import { | import { | ||||
| useConnectToKnowledge, | useConnectToKnowledge, | ||||
| useCreateFolder, | useCreateFolder, | ||||
| useSelectParentFolderList, | useSelectParentFolderList, | ||||
| useUploadFile, | useUploadFile, | ||||
| } from '@/hooks/fileManagerHooks'; | } from '@/hooks/fileManagerHooks'; | ||||
| import { useGetPagination, useSetPagination } from '@/hooks/logicHooks'; | |||||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | ||||
| import { Pagination } from '@/interfaces/common'; | |||||
| import { IFile } from '@/interfaces/database/file-manager'; | import { IFile } from '@/interfaces/database/file-manager'; | ||||
| import { getFilePathByWebkitRelativePath } from '@/utils/fileUtil'; | |||||
| import { PaginationProps } from 'antd'; | import { PaginationProps } from 'antd'; | ||||
| import { UploadFile } from 'antd/lib'; | import { UploadFile } from 'antd/lib'; | ||||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | import { useCallback, useEffect, useMemo, useState } from 'react'; | ||||
| const [searchParams] = useSearchParams(); | const [searchParams] = useSearchParams(); | ||||
| const id = searchParams.get('folderId') as string; | const id = searchParams.get('folderId') as string; | ||||
| return id; | |||||
| return id ?? ''; | |||||
| }; | }; | ||||
| export const useFetchDocumentListOnMount = () => { | export const useFetchDocumentListOnMount = () => { | ||||
| const fetchDocumentList = useFetchFileList(); | const fetchDocumentList = useFetchFileList(); | ||||
| const fileList = useSelectFileList(); | const fileList = useSelectFileList(); | ||||
| const id = useGetFolderId(); | const id = useGetFolderId(); | ||||
| const { searchString, pagination } = useSelector( | |||||
| (state) => state.fileManager, | |||||
| ); | |||||
| const { pageSize, current } = pagination; | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchDocumentList({ parent_id: id }); | |||||
| }, [dispatch, fetchDocumentList, id]); | |||||
| fetchDocumentList({ | |||||
| parent_id: id, | |||||
| keywords: searchString, | |||||
| page_size: pageSize, | |||||
| page: current, | |||||
| }); | |||||
| }, [dispatch, fetchDocumentList, id, current, pageSize, searchString]); | |||||
| return { fetchDocumentList, fileList }; | return { fetchDocumentList, fileList }; | ||||
| }; | }; | ||||
| export const useGetPagination = ( | |||||
| fetchDocumentList: (payload: IFile) => any, | |||||
| ) => { | |||||
| const dispatch = useDispatch(); | |||||
| const kFModel = useSelector((state: any) => state.kFModel); | |||||
| const { t } = useTranslate('common'); | |||||
| const setPagination = useCallback( | |||||
| (pageNumber = 1, pageSize?: number) => { | |||||
| const pagination: Pagination = { | |||||
| current: pageNumber, | |||||
| } as Pagination; | |||||
| if (pageSize) { | |||||
| pagination.pageSize = pageSize; | |||||
| } | |||||
| dispatch({ | |||||
| type: 'kFModel/setPagination', | |||||
| payload: pagination, | |||||
| }); | |||||
| }, | |||||
| [dispatch], | |||||
| ); | |||||
| export const useGetFilesPagination = () => { | |||||
| const { pagination } = useSelector((state) => state.fileManager); | |||||
| const setPagination = useSetPagination('fileManager'); | |||||
| const onPageChange: PaginationProps['onChange'] = useCallback( | const onPageChange: PaginationProps['onChange'] = useCallback( | ||||
| (pageNumber: number, pageSize: number) => { | (pageNumber: number, pageSize: number) => { | ||||
| setPagination(pageNumber, pageSize); | setPagination(pageNumber, pageSize); | ||||
| fetchDocumentList(); | |||||
| }, | }, | ||||
| [fetchDocumentList, setPagination], | |||||
| [setPagination], | |||||
| ); | ); | ||||
| const pagination: PaginationProps = useMemo(() => { | |||||
| return { | |||||
| showQuickJumper: true, | |||||
| total: kFModel.total, | |||||
| showSizeChanger: true, | |||||
| current: kFModel.pagination.current, | |||||
| pageSize: kFModel.pagination.pageSize, | |||||
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |||||
| onChange: onPageChange, | |||||
| showTotal: (total) => `${t('total')} ${total}`, | |||||
| }; | |||||
| }, [kFModel, onPageChange, t]); | |||||
| const { pagination: paginationInfo } = useGetPagination( | |||||
| pagination.total, | |||||
| pagination.current, | |||||
| pagination.pageSize, | |||||
| onPageChange, | |||||
| ); | |||||
| return { | return { | ||||
| pagination, | |||||
| pagination: paginationInfo, | |||||
| setPagination, | setPagination, | ||||
| total: kFModel.total, | |||||
| searchString: kFModel.searchString, | |||||
| }; | }; | ||||
| }; | }; | ||||
| export const useHandleSearchChange = (setPagination: () => void) => { | |||||
| export const useHandleSearchChange = () => { | |||||
| const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
| const throttledGetDocumentList = useCallback(() => { | |||||
| dispatch({ | |||||
| type: 'kFModel/throttledGetDocumentList', | |||||
| }); | |||||
| }, [dispatch]); | |||||
| const { searchString } = useSelector((state) => state.fileManager); | |||||
| const setPagination = useSetPagination('fileManager'); | |||||
| const handleInputChange = useCallback( | const handleInputChange = useCallback( | ||||
| (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | ||||
| const value = e.target.value; | const value = e.target.value; | ||||
| dispatch({ type: 'kFModel/setSearchString', payload: value }); | |||||
| dispatch({ type: 'fileManager/setSearchString', payload: value }); | |||||
| setPagination(); | setPagination(); | ||||
| throttledGetDocumentList(); | |||||
| }, | }, | ||||
| [setPagination, throttledGetDocumentList, dispatch], | |||||
| [setPagination, dispatch], | |||||
| ); | ); | ||||
| return { handleInputChange }; | |||||
| return { handleInputChange, searchString }; | |||||
| }; | }; | ||||
| export const useGetRowSelection = () => { | export const useGetRowSelection = () => { | ||||
| }, | }, | ||||
| }; | }; | ||||
| return rowSelection; | |||||
| return { rowSelection, setSelectedRowKeys }; | |||||
| }; | }; | ||||
| export const useNavigateToOtherFolder = () => { | export const useNavigateToOtherFolder = () => { | ||||
| }; | }; | ||||
| }; | }; | ||||
| export const useHandleDeleteFile = (fileIds: string[]) => { | |||||
| export const useHandleDeleteFile = ( | |||||
| fileIds: string[], | |||||
| setSelectedRowKeys: (keys: string[]) => void, | |||||
| ) => { | |||||
| const removeDocument = useRemoveFile(); | const removeDocument = useRemoveFile(); | ||||
| const showDeleteConfirm = useShowDeleteConfirm(); | const showDeleteConfirm = useShowDeleteConfirm(); | ||||
| const parentId = useGetFolderId(); | const parentId = useGetFolderId(); | ||||
| const handleRemoveFile = () => { | const handleRemoveFile = () => { | ||||
| showDeleteConfirm({ | showDeleteConfirm({ | ||||
| onOk: () => { | |||||
| return removeDocument(fileIds, parentId); | |||||
| onOk: async () => { | |||||
| const retcode = await removeDocument(fileIds, parentId); | |||||
| if (retcode === 0) { | |||||
| setSelectedRowKeys([]); | |||||
| } | |||||
| return; | |||||
| }, | }, | ||||
| }); | }); | ||||
| }; | }; | ||||
| async (fileList: UploadFile[]) => { | async (fileList: UploadFile[]) => { | ||||
| console.info('fileList', fileList); | console.info('fileList', fileList); | ||||
| if (fileList.length > 0) { | if (fileList.length > 0) { | ||||
| const ret = await uploadFile( | |||||
| fileList[0], | |||||
| id, | |||||
| getFilePathByWebkitRelativePath(fileList[0] as any), | |||||
| ); | |||||
| const ret = await uploadFile(fileList, id); | |||||
| console.info(ret); | |||||
| if (ret === 0) { | if (ret === 0) { | ||||
| hideFileUploadModal(); | hideFileUploadModal(); | ||||
| } | } | ||||
| } = useSetModalState(); | } = useSetModalState(); | ||||
| const connectToKnowledge = useConnectToKnowledge(); | const connectToKnowledge = useConnectToKnowledge(); | ||||
| const id = useGetFolderId(); | const id = useGetFolderId(); | ||||
| const [fileIds, setFileIds] = useState<string[]>([]); | |||||
| const [record, setRecord] = useState<IFile>({} as IFile); | |||||
| const initialValue = useMemo(() => { | |||||
| return Array.isArray(record?.kbs_info) | |||||
| ? record?.kbs_info?.map((x) => x.kb_id) | |||||
| : []; | |||||
| }, [record?.kbs_info]); | |||||
| const onConnectToKnowledgeOk = useCallback( | const onConnectToKnowledgeOk = useCallback( | ||||
| async (knowledgeIds: string[]) => { | async (knowledgeIds: string[]) => { | ||||
| const ret = await connectToKnowledge({ | const ret = await connectToKnowledge({ | ||||
| parentId: id, | parentId: id, | ||||
| fileIds, | |||||
| fileIds: [record.id], | |||||
| kbIds: knowledgeIds, | kbIds: knowledgeIds, | ||||
| }); | }); | ||||
| hideConnectToKnowledgeModal(); | hideConnectToKnowledgeModal(); | ||||
| } | } | ||||
| }, | }, | ||||
| [connectToKnowledge, hideConnectToKnowledgeModal, id, fileIds], | |||||
| [connectToKnowledge, hideConnectToKnowledgeModal, id, record.id], | |||||
| ); | ); | ||||
| const loading = useOneNamespaceEffectsLoading('fileManager', [ | const loading = useOneNamespaceEffectsLoading('fileManager', [ | ||||
| ]); | ]); | ||||
| const handleShowConnectToKnowledgeModal = useCallback( | const handleShowConnectToKnowledgeModal = useCallback( | ||||
| (ids: string[]) => { | |||||
| setFileIds(ids); | |||||
| (record: IFile) => { | |||||
| setRecord(record); | |||||
| showConnectToKnowledgeModal(); | showConnectToKnowledgeModal(); | ||||
| }, | }, | ||||
| [showConnectToKnowledgeModal], | [showConnectToKnowledgeModal], | ||||
| ); | ); | ||||
| return { | return { | ||||
| initialValue, | |||||
| connectToKnowledgeLoading: loading, | connectToKnowledgeLoading: loading, | ||||
| onConnectToKnowledgeOk, | onConnectToKnowledgeOk, | ||||
| connectToKnowledgeVisible, | connectToKnowledgeVisible, |
| width: 22px; | width: 22px; | ||||
| text-align: center; | text-align: center; | ||||
| } | } | ||||
| .linkButton { | |||||
| padding: 0; | |||||
| } |
| import { useSelectFileList } from '@/hooks/fileManagerHooks'; | import { useSelectFileList } from '@/hooks/fileManagerHooks'; | ||||
| import { IFile } from '@/interfaces/database/file-manager'; | import { IFile } from '@/interfaces/database/file-manager'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { Button, Table } from 'antd'; | |||||
| import { Button, Flex, Table } from 'antd'; | |||||
| import { ColumnsType } from 'antd/es/table'; | import { ColumnsType } from 'antd/es/table'; | ||||
| import ActionCell from './action-cell'; | import ActionCell from './action-cell'; | ||||
| import FileToolbar from './file-toolbar'; | import FileToolbar from './file-toolbar'; | ||||
| import { | import { | ||||
| useGetFilesPagination, | |||||
| useGetRowSelection, | useGetRowSelection, | ||||
| useHandleConnectToKnowledge, | useHandleConnectToKnowledge, | ||||
| useHandleCreateFolder, | useHandleCreateFolder, | ||||
| } from './hooks'; | } from './hooks'; | ||||
| import RenameModal from '@/components/rename-modal'; | import RenameModal from '@/components/rename-modal'; | ||||
| import SvgIcon from '@/components/svg-icon'; | |||||
| import { getExtension } from '@/utils/documentUtils'; | |||||
| import ConnectToKnowledgeModal from './connect-to-knowledge-modal'; | import ConnectToKnowledgeModal from './connect-to-knowledge-modal'; | ||||
| import FileUploadModal from './file-upload-modal'; | import FileUploadModal from './file-upload-modal'; | ||||
| import FolderCreateModal from './folder-create-modal'; | import FolderCreateModal from './folder-create-modal'; | ||||
| const FileManager = () => { | const FileManager = () => { | ||||
| const fileList = useSelectFileList(); | const fileList = useSelectFileList(); | ||||
| const rowSelection = useGetRowSelection(); | |||||
| const { rowSelection, setSelectedRowKeys } = useGetRowSelection(); | |||||
| const loading = useSelectFileListLoading(); | const loading = useSelectFileListLoading(); | ||||
| const navigateToOtherFolder = useNavigateToOtherFolder(); | const navigateToOtherFolder = useNavigateToOtherFolder(); | ||||
| const { | const { | ||||
| folderCreateLoading, | folderCreateLoading, | ||||
| onFolderCreateOk, | onFolderCreateOk, | ||||
| } = useHandleCreateFolder(); | } = useHandleCreateFolder(); | ||||
| const { fileUploadVisible, hideFileUploadModal, showFileUploadModal } = | |||||
| useHandleUploadFile(); | |||||
| const { | |||||
| fileUploadVisible, | |||||
| hideFileUploadModal, | |||||
| showFileUploadModal, | |||||
| fileUploadLoading, | |||||
| onFileUploadOk, | |||||
| } = useHandleUploadFile(); | |||||
| const { | const { | ||||
| connectToKnowledgeVisible, | connectToKnowledgeVisible, | ||||
| hideConnectToKnowledgeModal, | hideConnectToKnowledgeModal, | ||||
| showConnectToKnowledgeModal, | showConnectToKnowledgeModal, | ||||
| onConnectToKnowledgeOk, | onConnectToKnowledgeOk, | ||||
| initialValue, | |||||
| } = useHandleConnectToKnowledge(); | } = useHandleConnectToKnowledge(); | ||||
| const { pagination } = useGetFilesPagination(); | |||||
| const columns: ColumnsType<IFile> = [ | const columns: ColumnsType<IFile> = [ | ||||
| { | { | ||||
| dataIndex: 'name', | dataIndex: 'name', | ||||
| key: 'name', | key: 'name', | ||||
| render(value, record) { | render(value, record) { | ||||
| return record.type === 'folder' ? ( | |||||
| <Button | |||||
| type={'link'} | |||||
| onClick={() => navigateToOtherFolder(record.id)} | |||||
| > | |||||
| {value} | |||||
| </Button> | |||||
| ) : ( | |||||
| value | |||||
| return ( | |||||
| <Flex gap={10} align="center"> | |||||
| <SvgIcon | |||||
| name={`file-icon/${record.type === 'folder' ? 'folder' : getExtension(value)}`} | |||||
| width={24} | |||||
| ></SvgIcon> | |||||
| {record.type === 'folder' ? ( | |||||
| <Button | |||||
| type={'link'} | |||||
| className={styles.linkButton} | |||||
| onClick={() => navigateToOtherFolder(record.id)} | |||||
| > | |||||
| {value} | |||||
| </Button> | |||||
| ) : ( | |||||
| value | |||||
| )} | |||||
| </Flex> | |||||
| ); | ); | ||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| title: 'kbs_info', | |||||
| title: 'Knowledge Base', | |||||
| dataIndex: 'kbs_info', | dataIndex: 'kbs_info', | ||||
| key: 'kbs_info', | key: 'kbs_info', | ||||
| render(value) { | render(value) { | ||||
| console.info(value); | |||||
| return Array.isArray(value) | return Array.isArray(value) | ||||
| ? value?.map((x) => x.kb_name).join(',') | ? value?.map((x) => x.kb_name).join(',') | ||||
| : ''; | : ''; | ||||
| }} | }} | ||||
| showRenameModal={showFileRenameModal} | showRenameModal={showFileRenameModal} | ||||
| showConnectToKnowledgeModal={showConnectToKnowledgeModal} | showConnectToKnowledgeModal={showConnectToKnowledgeModal} | ||||
| setSelectedRowKeys={setSelectedRowKeys} | |||||
| ></ActionCell> | ></ActionCell> | ||||
| ), | ), | ||||
| }, | }, | ||||
| selectedRowKeys={rowSelection.selectedRowKeys as string[]} | selectedRowKeys={rowSelection.selectedRowKeys as string[]} | ||||
| showFolderCreateModal={showFolderCreateModal} | showFolderCreateModal={showFolderCreateModal} | ||||
| showFileUploadModal={showFileUploadModal} | showFileUploadModal={showFileUploadModal} | ||||
| setSelectedRowKeys={setSelectedRowKeys} | |||||
| ></FileToolbar> | ></FileToolbar> | ||||
| <Table | <Table | ||||
| dataSource={fileList} | dataSource={fileList} | ||||
| rowKey={'id'} | rowKey={'id'} | ||||
| rowSelection={rowSelection} | rowSelection={rowSelection} | ||||
| loading={loading} | loading={loading} | ||||
| pagination={pagination} | |||||
| /> | /> | ||||
| <RenameModal | <RenameModal | ||||
| visible={fileRenameVisible} | visible={fileRenameVisible} | ||||
| <FileUploadModal | <FileUploadModal | ||||
| visible={fileUploadVisible} | visible={fileUploadVisible} | ||||
| hideModal={hideFileUploadModal} | hideModal={hideFileUploadModal} | ||||
| loading={fileUploadLoading} | |||||
| onOk={onFileUploadOk} | |||||
| ></FileUploadModal> | ></FileUploadModal> | ||||
| <ConnectToKnowledgeModal | <ConnectToKnowledgeModal | ||||
| initialValue={initialValue} | |||||
| visible={connectToKnowledgeVisible} | visible={connectToKnowledgeVisible} | ||||
| hideModal={hideConnectToKnowledgeModal} | hideModal={hideConnectToKnowledgeModal} | ||||
| onOk={onConnectToKnowledgeOk} | onOk={onConnectToKnowledgeOk} |
| import { paginationModel } from '@/base'; | |||||
| import { BaseState } from '@/interfaces/common'; | |||||
| import { IFile, IFolder } from '@/interfaces/database/file-manager'; | import { IFile, IFolder } from '@/interfaces/database/file-manager'; | ||||
| import fileManagerService from '@/services/fileManagerService'; | import fileManagerService from '@/services/fileManagerService'; | ||||
| import omit from 'lodash/omit'; | import omit from 'lodash/omit'; | ||||
| import { DvaModel } from 'umi'; | import { DvaModel } from 'umi'; | ||||
| export interface FileManagerModelState { | |||||
| export interface FileManagerModelState extends BaseState { | |||||
| fileList: IFile[]; | fileList: IFile[]; | ||||
| parentFolderList: IFolder[]; | parentFolderList: IFolder[]; | ||||
| } | } | ||||
| const model: DvaModel<FileManagerModelState> = { | const model: DvaModel<FileManagerModelState> = { | ||||
| namespace: 'fileManager', | namespace: 'fileManager', | ||||
| state: { fileList: [], parentFolderList: [] }, | |||||
| state: { | |||||
| fileList: [], | |||||
| parentFolderList: [], | |||||
| ...(paginationModel.state as BaseState), | |||||
| }, | |||||
| reducers: { | reducers: { | ||||
| setFileList(state, { payload }) { | setFileList(state, { payload }) { | ||||
| return { ...state, fileList: payload }; | return { ...state, fileList: payload }; | ||||
| setParentFolderList(state, { payload }) { | setParentFolderList(state, { payload }) { | ||||
| return { ...state, parentFolderList: payload }; | return { ...state, parentFolderList: payload }; | ||||
| }, | }, | ||||
| ...paginationModel.reducers, | |||||
| }, | }, | ||||
| effects: { | effects: { | ||||
| *removeFile({ payload = {} }, { call, put }) { | *removeFile({ payload = {} }, { call, put }) { | ||||
| payload: { parentId: payload.parentId }, | payload: { parentId: payload.parentId }, | ||||
| }); | }); | ||||
| } | } | ||||
| return retcode; | |||||
| }, | }, | ||||
| *listFile({ payload = {} }, { call, put }) { | |||||
| const { data } = yield call(fileManagerService.listFile, payload); | |||||
| *listFile({ payload = {} }, { call, put, select }) { | |||||
| const { searchString, pagination } = yield select( | |||||
| (state: any) => state.fileManager, | |||||
| ); | |||||
| const { current, pageSize } = pagination; | |||||
| const { data } = yield call(fileManagerService.listFile, { | |||||
| ...payload, | |||||
| keywords: searchString.trim(), | |||||
| page: current, | |||||
| pageSize, | |||||
| }); | |||||
| const { retcode, data: res } = data; | const { retcode, data: res } = data; | ||||
| if (retcode === 0 && Array.isArray(res.files)) { | if (retcode === 0 && Array.isArray(res.files)) { | ||||
| yield put({ | yield put({ | ||||
| type: 'setFileList', | type: 'setFileList', | ||||
| payload: res.files, | payload: res.files, | ||||
| }); | }); | ||||
| yield put({ | |||||
| type: 'setPagination', | |||||
| payload: { total: res.total }, | |||||
| }); | |||||
| } | } | ||||
| }, | }, | ||||
| *renameFile({ payload = {} }, { call, put }) { | *renameFile({ payload = {} }, { call, put }) { | ||||
| return data.retcode; | return data.retcode; | ||||
| }, | }, | ||||
| *uploadFile({ payload = {} }, { call, put }) { | *uploadFile({ payload = {} }, { call, put }) { | ||||
| const fileList = payload.file; | |||||
| const pathList = payload.path; | |||||
| const formData = new FormData(); | const formData = new FormData(); | ||||
| formData.append('parent_id', payload.parentId); | formData.append('parent_id', payload.parentId); | ||||
| formData.append('file', payload.file); | |||||
| formData.append('path', payload.path); | |||||
| // formData.append('file', payload.file); | |||||
| // formData.append('path', payload.path); | |||||
| fileList.forEach((file: any, index: number) => { | |||||
| formData.append('file', file); | |||||
| formData.append('path', pathList[index]); | |||||
| }); | |||||
| const { data } = yield call(fileManagerService.uploadFile, formData); | const { data } = yield call(fileManagerService.uploadFile, formData); | ||||
| if (data.retcode === 0) { | if (data.retcode === 0) { | ||||
| yield put({ | yield put({ | ||||
| }, | }, | ||||
| }, | }, | ||||
| }; | }; | ||||
| // const finalModel = modelExtend<DvaModel<FileManagerModelState & BaseState>>( | |||||
| // paginationModel, | |||||
| // model, | |||||
| // ); | |||||
| // console.info(finalModel); | |||||
| export default model; | export default model; |
| import { DvaModel } from 'umi'; | import { DvaModel } from 'umi'; | ||||
| export interface KnowledgeModelState { | export interface KnowledgeModelState { | ||||
| data: any[]; | |||||
| data: IKnowledge[]; | |||||
| knowledge: IKnowledge; | knowledge: IKnowledge; | ||||
| } | } | ||||
| downloadElement.click(); | downloadElement.click(); | ||||
| document.body.removeChild(downloadElement); | document.body.removeChild(downloadElement); | ||||
| }; | }; | ||||
| export const getFilePathByWebkitRelativePath = (file: File) => { | |||||
| const path = file.webkitRelativePath; | |||||
| return path; | |||||
| // if (path !== '') { | |||||
| // return path.slice(0, path.lastIndexOf('/')); | |||||
| // } | |||||
| // return path; | |||||
| }; |