Browse Source

fix: fix uploaded file time error #680 (#690)

### What problem does this PR solve?

fix: fix uploaded file time error #680
feat: support preview of word and excel #684 

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
tags/v0.6.0
balibabu 1 year ago
parent
commit
793e29f23a
No account linked to committer's email address

+ 6
- 4
web/src/components/new-document-link.tsx View File

import { api_host } from '@/utils/api';
import React from 'react'; import React from 'react';


interface IProps extends React.PropsWithChildren { interface IProps extends React.PropsWithChildren {
documentId: string;
link: string;
preventDefault?: boolean; preventDefault?: boolean;
color?: string;
} }


const NewDocumentLink = ({ const NewDocumentLink = ({
children, children,
documentId,
link,
preventDefault = false, preventDefault = false,
color = 'rgb(15, 79, 170)',
}: IProps) => { }: IProps) => {
return ( return (
<a <a
target="_blank" target="_blank"
onClick={!preventDefault ? undefined : (e) => e.preventDefault()} onClick={!preventDefault ? undefined : (e) => e.preventDefault()}
href={`${api_host}/document/get/${documentId}`}
href={link}
rel="noreferrer" rel="noreferrer"
style={{ color }}
> >
{children} {children}
</a> </a>

+ 20
- 0
web/src/constants/common.ts View File

xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
mp4: 'video/mp4', mp4: 'video/mp4',
}; };

//#region file preview
export const Images = [
'jpg',
'jpeg',
'png',
'gif',
'bmp',
'tif',
'tiff',
'webp',
// 'svg',
'ico',
];

// Without FileViewer
export const ExceptiveType = ['xlsx', 'xls', 'pdf', ...Images];

export const SupportedPreviewDocumentTypes = ['docx', 'csv', ...ExceptiveType];
//#endregion

+ 8
- 5
web/src/hooks/documentHooks.ts View File

import { useGetKnowledgeSearchParams } from './routeHook'; import { useGetKnowledgeSearchParams } from './routeHook';
import { useOneNamespaceEffectsLoading } from './storeHooks'; import { useOneNamespaceEffectsLoading } from './storeHooks';


export const useGetDocumentUrl = (documentId: string) => {
const url = useMemo(() => {
return `${api_host}/document/get/${documentId}`;
}, [documentId]);
export const useGetDocumentUrl = (documentId?: string) => {
const getDocumentUrl = useCallback(
(id?: string) => {
return `${api_host}/document/get/${documentId || id}`;
},
[documentId],
);


return url;
return getDocumentUrl;
}; };


export const useGetChunkHighlights = (selectedChunk: IChunk) => { export const useGetChunkHighlights = (selectedChunk: IChunk) => {

+ 1
- 0
web/src/locales/en.ts View File

'Support for a single or bulk upload. Strictly prohibited from uploading company data or other banned files.', 'Support for a single or bulk upload. Strictly prohibited from uploading company data or other banned files.',
local: 'Local uploads', local: 'Local uploads',
s3: 'S3 uploads', s3: 'S3 uploads',
preview: 'Preview',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

+ 1
- 0
web/src/locales/zh-traditional.ts View File

directory: '文件夾', directory: '文件夾',
local: '本地上傳', local: '本地上傳',
s3: 'S3 上傳', s3: 'S3 上傳',
preview: '預覽',
}, },
footer: { footer: {
profile: '“保留所有權利 @ react”', profile: '“保留所有權利 @ react”',

+ 1
- 0
web/src/locales/zh.ts View File

directory: '文件夹', directory: '文件夹',
local: '本地上传', local: '本地上传',
s3: 'S3 上传', s3: 'S3 上传',
preview: '预览',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

+ 2
- 2
web/src/pages/add-knowledge/components/knowledge-file/index.tsx View File

}, },
{ {
title: t('uploadDate'), title: t('uploadDate'),
dataIndex: 'create_date',
key: 'create_date',
dataIndex: 'create_time',
key: 'create_time',
render(value) { render(value) {
return formatDate(value); return formatDate(value);
}, },

+ 6
- 1
web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx View File

import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg'; import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg';
import NewDocumentLink from '@/components/new-document-link'; import NewDocumentLink from '@/components/new-document-link';
import { useGetDocumentUrl } from '@/hooks/documentHooks';
import { ITestingDocument } from '@/interfaces/database/knowledge'; import { ITestingDocument } from '@/interfaces/database/knowledge';
import { isPdf } from '@/utils/documentUtils'; import { isPdf } from '@/utils/documentUtils';
import { Table, TableProps } from 'antd'; import { Table, TableProps } from 'antd';
); );


const dispatch = useDispatch(); const dispatch = useDispatch();
const getDocumentUrl = useGetDocumentUrl();


const columns: TableProps<ITestingDocument>['columns'] = [ const columns: TableProps<ITestingDocument>['columns'] = [
{ {
key: 'view', key: 'view',
width: 50, width: 50,
render: (_, { doc_id, doc_name }) => ( render: (_, { doc_id, doc_name }) => (
<NewDocumentLink documentId={doc_id} preventDefault={!isPdf(doc_name)}>
<NewDocumentLink
link={getDocumentUrl(doc_id)}
preventDefault={!isPdf(doc_name)}
>
<NavigationPointerIcon /> <NavigationPointerIcon />
</NewDocumentLink> </NewDocumentLink>
), ),

+ 3
- 1
web/src/pages/chat/chat-container/index.tsx View File



import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { useTranslate } from '@/hooks/commonHooks'; import { useTranslate } from '@/hooks/commonHooks';
import { useGetDocumentUrl } from '@/hooks/documentHooks';
import { getExtension, isPdf } from '@/utils/documentUtils'; import { getExtension, isPdf } from '@/utils/documentUtils';
import styles from './index.less'; import styles from './index.less';


}) => { }) => {
const userInfo = useSelectUserInfo(); const userInfo = useSelectUserInfo();
const fileThumbnails = useSelectFileThumbnails(); const fileThumbnails = useSelectFileThumbnails();
const getDocumentUrl = useGetDocumentUrl();


const isAssistant = item.role === MessageType.Assistant; const isAssistant = item.role === MessageType.Assistant;


)} )}


<NewDocumentLink <NewDocumentLink
documentId={item.doc_id}
link={getDocumentUrl(item.doc_id)}
preventDefault={!isPdf(item.doc_name)} preventDefault={!isPdf(item.doc_name)}
> >
{item.doc_name} {item.doc_name}

+ 5
- 0
web/src/pages/document-viewer/index.less View File

.viewerWrapper { .viewerWrapper {
width: 100%; width: 100%;
height: 100%;
:global { :global {
.pdf-canvas { .pdf-canvas {
text-align: center; text-align: center;
} }
} }
.image {
width: 100%;
height: 100%;
}
} }

+ 15
- 2
web/src/pages/document-viewer/index.tsx View File

import { ExceptiveType, Images } from '@/constants/common';
import { api_host } from '@/utils/api'; import { api_host } from '@/utils/api';
import { Flex, Image } from 'antd';
import FileViewer from 'react-file-viewer'; import FileViewer from 'react-file-viewer';
import { useParams, useSearchParams } from 'umi'; import { useParams, useSearchParams } from 'umi';
import Excel from './excel'; import Excel from './excel';
import Pdf from './pdf';


import styles from './index.less'; import styles from './index.less';


// TODO: The interface returns an incorrect content-type for the SVG.

const isNotExceptiveType = (ext: string) => ExceptiveType.indexOf(ext) === -1;

const DocumentViewer = () => { const DocumentViewer = () => {
const { id: documentId } = useParams(); const { id: documentId } = useParams();
const api = `${api_host}/file/get/${documentId}`; const api = `${api_host}/file/get/${documentId}`;


return ( return (
<section className={styles.viewerWrapper}> <section className={styles.viewerWrapper}>
{ext === 'xlsx' && <Excel filePath={api}></Excel>}
{ext !== 'xlsx' && (
{Images.includes(ext!) && (
<Flex className={styles.image} align="center" justify="center">
<Image src={api} preview={false}></Image>
</Flex>
)}
{ext === 'pdf' && <Pdf url={api}></Pdf>}
{(ext === 'xlsx' || ext === 'xls') && <Excel filePath={api}></Excel>}
{isNotExceptiveType(ext!) && (
<FileViewer fileType={ext} filePath={api} onError={onError} /> <FileViewer fileType={ext} filePath={api} onError={onError} />
)} )}
</section> </section>

+ 38
- 0
web/src/pages/document-viewer/pdf/index.tsx View File

import { Skeleton } from 'antd';
import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter';

interface IProps {
url: string;
}

const DocumentPreviewer = ({ url }: IProps) => {
const resetHash = () => {};

return (
<div style={{ width: '100%' }}>
<PdfLoader
url={url}
beforeLoad={<Skeleton active />}
workerSrc="/pdfjs-dist/pdf.worker.min.js"
>
{(pdfDocument) => {
return (
<PdfHighlighter
pdfDocument={pdfDocument}
enableAreaSelection={(event) => event.altKey}
onScrollChange={resetHash}
scrollRef={() => {}}
onSelectionFinished={() => null}
highlightTransform={() => {
return <div></div>;
}}
highlights={[]}
/>
);
}}
</PdfLoader>
</div>
);
};

export default DocumentPreviewer;

+ 22
- 11
web/src/pages/file-manager/action-cell/index.tsx View File

DeleteOutlined, DeleteOutlined,
DownloadOutlined, DownloadOutlined,
EditOutlined, EditOutlined,
EyeOutlined,
LinkOutlined, LinkOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Button, Space, Tooltip } from 'antd'; import { Button, Space, Tooltip } from 'antd';
import { useHandleDeleteFile, useNavigateToDocument } from '../hooks';
import { useHandleDeleteFile } from '../hooks';


import NewDocumentLink from '@/components/new-document-link';
import { SupportedPreviewDocumentTypes } from '@/constants/common';
import { getExtension } from '@/utils/documentUtils';
import styles from './index.less'; import styles from './index.less';


const isSupportedPreviewDocumentType = (fileExtension: string) => {
return SupportedPreviewDocumentTypes.includes(fileExtension);
};

interface IProps { interface IProps {
record: IFile; record: IFile;
setCurrentRecord: (record: any) => void; setCurrentRecord: (record: any) => void;
[documentId], [documentId],
setSelectedRowKeys, setSelectedRowKeys,
); );
const navigateToDocument = useNavigateToDocument(record.id, record.name);
const extension = getExtension(record.name);


const onDownloadDocument = () => { const onDownloadDocument = () => {
downloadFile({ downloadFile({


return ( return (
<Space size={0}> <Space size={0}>
{/* <Tooltip title={t('addToKnowledge')}>
<Button
type="text"
className={styles.iconButton}
onClick={navigateToDocument}
>
<EyeOutlined size={20} />
</Button>
</Tooltip> */}
<Tooltip title={t('addToKnowledge')}> <Tooltip title={t('addToKnowledge')}>
<Button <Button
type="text" type="text"
</Button> </Button>
</Tooltip> </Tooltip>
)} )}
{isSupportedPreviewDocumentType(extension) && (
<NewDocumentLink
color="black"
link={`/document/${documentId}?ext=${extension}`}
>
<Tooltip title={t('preview')}>
<Button type="text" className={styles.iconButton}>
<EyeOutlined size={20} />
</Button>
</Tooltip>
</NewDocumentLink>
)}
</Space> </Space>
); );
}; };

+ 0
- 10
web/src/pages/file-manager/hooks.ts View File

import { useGetPagination, useSetPagination } from '@/hooks/logicHooks'; import { useGetPagination, useSetPagination } from '@/hooks/logicHooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { IFile } from '@/interfaces/database/file-manager'; import { IFile } from '@/interfaces/database/file-manager';
import { getExtension } from '@/utils/documentUtils';
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';


return { handleBreadcrumbClick }; return { handleBreadcrumbClick };
}; };

export const useNavigateToDocument = (documentId: string, name: string) => {
const navigate = useNavigate();
const navigateToDocument = () => {
navigate(`/document/${documentId}?ext=${getExtension(name)}`);
};

return navigateToDocument;
};

+ 2
- 2
web/src/pages/file-manager/index.tsx View File

}, },
{ {
title: t('uploadDate'), title: t('uploadDate'),
dataIndex: 'create_date',
key: 'create_date',
dataIndex: 'create_time',
key: 'create_time',
render(text) { render(text) {
return formatDate(text); return formatDate(text);
}, },

+ 1
- 1
web/src/pages/knowledge/knowledge-card/index.tsx View File

<div className={styles.bottomLeft}> <div className={styles.bottomLeft}>
<CalendarOutlined className={styles.leftIcon} /> <CalendarOutlined className={styles.leftIcon} />
<span className={styles.rightText}> <span className={styles.rightText}>
{formatDate(item.update_date)}
{formatDate(item.update_time)}
</span> </span>
</div> </div>
{/* <Avatar.Group size={25}> {/* <Avatar.Group size={25}>

+ 5
- 4
web/src/routes.ts View File

path: '/flow', path: '/flow',
component: '@/pages/flow', component: '@/pages/flow',
}, },
{
path: 'document/:id',
component: '@/pages/document-viewer',
},
], ],
}, },
{
path: 'document/:id',
component: '@/pages/document-viewer',
layout: false,
},
{ {
path: '/*', path: '/*',
component: '@/pages/404', component: '@/pages/404',

+ 1
- 1
web/src/utils/date.ts View File

if (!date) { if (!date) {
return ''; return '';
} }
return dayjs(date).format('DD/MM/YYYY');
return dayjs(date).format('DD/MM/YYYY HH:mm:ss');
} }

Loading…
Cancel
Save