Skip to content

Commit

Permalink
✨ feat(ims-view-pc): add FileViewer
Browse files Browse the repository at this point in the history
  • Loading branch information
eternallycyf committed Dec 14, 2024
1 parent 5a621b0 commit de61b2c
Show file tree
Hide file tree
Showing 11 changed files with 637 additions and 1 deletion.
217 changes: 217 additions & 0 deletions packages/ims-view-pc/src/components/FileViewer/FileView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { Image, Skeleton } from 'antd';
import cx from 'classnames';
import { renderAsync } from 'docx-preview';
import type { CSSProperties } from 'react';
import { PureComponent } from 'react';
import { OutTable } from 'react-excel-renderer';
import './index.less';
import MarkDown from './MarkDown';

interface IProps {
styles?: CSSProperties;
src?: string;
base64?: string;
[onherProps: string]: any;
}

const txtFileTypes = [
'txt',
'json',
'js',
'css',
'java',
'py',
'html',
'jsx',
'ts',
'tsx',
'xml',
'md',
'log',
];

class FileView extends PureComponent<IProps, any> {
constructor(props: IProps | Readonly<IProps>) {
super(props);
this.state = {
pdfSrc: '',
loading: false,
text: '',
};
}
componentDidMount() {
const { src, base64 } = this.props;
if (src) {
this.showPDFBySrc(src);
} else if (base64) {
this.showPDFByBase64(base64);
}
}
componentwillUnmount() {
URL.revokeObjectURL(this.state.pdfSrc);
}
//通过src展示
showPDFBySrc = async (src: string) => {
const { fileType } = this.props;
if (fileType == 'pdf') {
try {
this.setState({
loading: true,
});
this.setState({
pdfSrc: src,
loading: false,
});
} catch (err) {}
}

if (fileType == 'word') {
// const url = `xxxxx/${src}`;
// const blob = await axios.get(url, {
// responseType: 'blob'
// })
// renderAsync(
// blob,
// document.getElementById("file-preview-modal") as HTMLDivElement,
// null as unknown as HTMLDivElement,
// {
// className: "docx", // 默认和文档样式类的类名/前缀
// inWrapper: true, // 启用围绕文档内容渲染包装器
// ignoreWidth: false, // 禁止页面渲染宽度
// ignoreHeight: false, // 禁止页面渲染高度
// ignoreFonts: false, // 禁止字体渲染
// breakPages: true, // 在分页符上启用分页
// ignoreLastRenderedPageBreak: true, //禁用lastRenderedPageBreak元素的分页
// experimental: false, //启用实验性功能(制表符停止计算)
// trimXmlDeclaration: true, //如果为真,xml声明将在解析之前从xml文档中删除
// debug: false, // 启用额外的日志记录
// },
// );
}
};

//base64转blob
dataURItoBlob = (dataURI: string) => {
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; // mime类型
const byteString = atob(dataURI.split(',')[1]); //base64 解码
const arrayBuffer = new ArrayBuffer(byteString.length); //创建缓冲数组
const intArray = new Uint8Array(arrayBuffer); //创建视图
for (let i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
return new Blob([intArray], { type: mimeString });
};

// 转baffer
readBuffer = async (file: any): Promise<Buffer> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (loadEvent: any) => resolve(loadEvent.target.result);
reader.onerror = (e) => reject(e);
reader.readAsArrayBuffer(file);
});
};

// 转文本string
readText = async (buffer: Buffer): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (loadEvent: any) => resolve(loadEvent.target.result);
reader.onerror = (e) => reject(e);
reader.readAsText(new Blob([buffer]), 'utf-8');
});
};

//通过base64展示
showPDFByBase64 = async (content: string) => {
const { fileType } = this.props;
const blob = this.dataURItoBlob(content);
const fileUrl = URL.createObjectURL(blob);
this.setState({
pdfSrc: fileUrl,
loading: false,
});

if (txtFileTypes.includes(fileType)) {
const Buffer = await this.readBuffer(blob);
const text = await this.readText(Buffer);
this.setState({
text,
});
}

// 当使用旧版浏览器时 需要兼容 globalThis
if (fileType === 'docx') {
setTimeout(() => {
renderAsync(
blob,
document.getElementById('file-preview-modal') as HTMLDivElement,
null as unknown as HTMLDivElement,
{
className: 'docx', // 默认和文档样式类的类名/前缀
inWrapper: true, // 启用围绕文档内容渲染包装器
ignoreWidth: false, // 禁止页面渲染宽度
ignoreHeight: false, // 禁止页面渲染高度
ignoreFonts: false, // 禁止字体渲染
breakPages: true, // 在分页符上启用分页
ignoreLastRenderedPageBreak: true, //禁用lastRenderedPageBreak元素的分页
experimental: false, //启用实验性功能(制表符停止计算)
trimXmlDeclaration: true, //如果为真,xml声明将在解析之前从xml文档中删除
debug: false, // 启用额外的日志记录
},
);
}, 0);
}
};

render() {
const { styles, className, fileType, txtFileTypes, excelData } = this.props;
const { loading, pdfSrc, text } = this.state;
const src = `${pdfSrc}`;

if (loading) {
return <Skeleton loading paragraph={{ rows: 10 }} active />;
}

if (fileType == 'docx') {
return <div id="file-preview-modal" />;
}

if (fileType == 'xlsx') {
return (
<OutTable
data={excelData.rows}
columns={excelData.cols}
tableClassName="ExcelTable2007"
tableHeaderRowClass="heading"
/>
);
}

if (fileType == 'png' || fileType == 'jpg') {
return (
<div className="image">
<Image src={src} />
</div>
);
}

if (txtFileTypes?.includes(fileType)) {
const newText = `
~~~${fileType}
${text}
~~~
`;
return <MarkDown markdown={newText} />;
}

if (fileType == 'pdf') {
return (
<iframe className={cx('iframeStyle', className)} style={styles} src={src} title="pdf预览" />
);
}

return <span>不支持</span>;
}
}
export default FileView;
99 changes: 99 additions & 0 deletions packages/ims-view-pc/src/components/FileViewer/FileViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { message, Modal } from 'antd';
import React, { PureComponent } from 'react';
import { ExcelRenderer } from 'react-excel-renderer';
import FileView from './FileView';
const txtFileTypes = [
'txt',
'json',
'js',
'css',
'java',
'py',
'html',
'jsx',
'ts',
'tsx',
'xml',
'md',
'log',
];

const fileAllTypes = ['docx', 'xlsx', 'png', 'jpg', 'pdf', ...txtFileTypes];
class FilePreView extends PureComponent<any, any> {
protected pdfViewRef: React.RefObject<any> = React.createRef();
protected previewWrapperRef: React.RefObject<HTMLDivElement> = React.createRef();
constructor(props: any) {
super(props);
this.state = {
modalVisible: false,
fileType: '',
excelData: {
cols: [],
rows: [],
},
};
}
//显隐状态的改变
controlIsShow = (params: { src?: string; base64?: string; originFileObj?: any }) => {
const { modalVisible } = this.state;
const { src, base64, originFileObj } = params;
const { name } = originFileObj;
const fileType = name.slice(name.lastIndexOf('.') + 1).toLowerCase();

if (!fileAllTypes.includes(fileType)) {
return message.error('该文件不支持预览');
}
if (fileType == 'xlsx') {
ExcelRenderer(originFileObj, (err: Error, resp: any) => {
this.setState({
excelData: {
cols: resp.cols,
rows: resp.rows,
},
});
});
}
this.setState({
modalVisible: !modalVisible,
src,
base64,
fileType,
});
};

render() {
const { modalVisible, src, base64, fileType, excelData } = this.state;
return (
<Modal
open={modalVisible}
title={fileType + ' 预览'}
onCancel={() => {
this.setState({
modalVisible: false,
});
}}
width={1200}
styles={{
body: {
overflow: 'scroll',
height: '70vh',
},
}}
footer={null}
destroyOnClose={true}
>
<FileView
id="file-preview-modal"
ref={this.pdfViewRef}
src={src}
base64={base64}
fileType={fileType}
excelData={excelData}
txtFileTypes={txtFileTypes}
styles={{ height: '600px' }}
/>
</Modal>
);
}
}
export default FilePreView;
34 changes: 34 additions & 0 deletions packages/ims-view-pc/src/components/FileViewer/MarkDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism';
import remarkGfm from 'remark-gfm';

export default (props: any) => {
return (
<ReactMarkdown
// eslint-disable-next-line react/no-children-prop
children={props.markdown}
remarkPlugins={[remarkGfm]}
components={{
code({ className, children }) {
const match = /language-(\w+)/.exec(className || '');
return match ? (
<SyntaxHighlighter
// eslint-disable-next-line react/no-children-prop
children={String(children).replace(/\n$/, '')}
style={dracula}
language={match[1]}
showLineNumbers
PreTag="div"
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
/>
);
};
Loading

0 comments on commit de61b2c

Please sign in to comment.