Skip to content

Commit 4b8dd7a

Browse files
author
吴多益
authored
feat: Office Viewer 支持 Excel (#9826)
* feat: Office viewer 支持 Excel * 修复类型报错 * 修复类型报错
1 parent ddee512 commit 4b8dd7a

File tree

701 files changed

+139153
-1925
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

701 files changed

+139153
-1925
lines changed

.editorconfig

+3
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ indent_size = 4
1616

1717
[*.md]
1818
trim_trailing_whitespace = false
19+
20+
[**.tsv]
21+
indent_style = tab

.gitattributes

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@
99
*.ttf binary
1010
*.woff binary
1111
*.docx binary
12+
*.xlsx binary
13+
*.pptx binary
14+
*.TTF binary
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
title: Office Viewer Excel
3+
description:
4+
type: 0
5+
group: ⚙ 组件
6+
menuName: OfficeViewer Excel 渲染
7+
icon:
8+
order: 24
9+
---
10+
11+
> 6.3 及以上版本
12+
13+
## 基本用法
14+
15+
```schema: scope="body"
16+
{
17+
"type": "office-viewer",
18+
"src": "/examples/static/all.xlsx",
19+
"excelOptions": {
20+
"height": 500
21+
}
22+
}
23+
```
24+
25+
除了 `xlsx`,也支持后缀为 `csv``tsv` 的文件
26+
27+
## 配置项
28+
29+
由于接口可能有变化,这里只列出少量配置项,后续补充
30+
31+
```schema: scope="body"
32+
{
33+
"type": "office-viewer",
34+
"excelOptions": {
35+
"showSheetTabBar": false,
36+
"showFormulaBar": false
37+
},
38+
"src": "/examples/static/all.xlsx"
39+
}
40+
```
41+
42+
| 属性名 | 类型 | 默认值 | 说明 |
43+
| --------------- | --------- | ------ | ------------------------ |
44+
| showFormulaBar | `boolean` | true | 是否显示公式拦 |
45+
| showSheetTabBar | `boolean` | true | 是否显示底部 sheet 切换 |
46+
| fontURL | `object` | | 字体地址,参考下面的说明 |
47+
48+
## 字体配置
49+
50+
由于浏览器中缺少特定字体,将展现会不一致,这些字体都是有版权的,因此本项目中不提供,需要自行准备,然后配置 `fontURL` 映射到对应的地址,渲染时就会加载。
51+
52+
类似如下配置
53+
54+
```json
55+
{
56+
"type": "office-viewer",
57+
"excelOptions": {
58+
"fontURL": {
59+
"等线": "/static/font/DengXian.ttf",
60+
"仿宋": "/static/font/STFANGSO.TTF",
61+
"黑体": "/static/font/simhei.ttf"
62+
}
63+
},
64+
"src": "/examples/static/all.xlsx"
65+
}
66+
```

docs/zh-CN/components/office-viewer.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ order: 23
1010

1111
> 2.9.0 及以上版本
1212
13-
用于渲染 office 文档,目前只支持 docx 格式
13+
用于渲染 office 文档,目前只支持 docx 和 xlsx 格式,本文档只介绍 docx 的配置,xlsx 的配置请参考 [office-viewer-excel](office-viewer-excel)
1414

1515
## 基本用法
1616

examples/components/Components.tsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -991,12 +991,21 @@ export const components = [
991991
)
992992
},
993993
{
994-
label: 'OfficeViewer 文档渲染',
994+
label: 'OfficeViewer Word 渲染',
995995
path: '/zh-CN/components/office-viewer',
996996
component: React.lazy(() =>
997997
import('../../docs/zh-CN/components/office-viewer.md').then(wrapDoc)
998998
)
999999
},
1000+
{
1001+
label: 'OfficeViewer Excel 渲染',
1002+
path: '/zh-CN/components/office-viewer-excel',
1003+
component: React.lazy(() =>
1004+
import('../../docs/zh-CN/components/office-viewer-excel.md').then(
1005+
wrapDoc
1006+
)
1007+
)
1008+
},
10001009
{
10011010
label: 'PDFViewer 渲染',
10021011
path: '/zh-CN/components/pdf-viewer',

examples/components/OfficeViewer.jsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ export default {
88
{
99
type: 'input-file',
1010
name: 'file',
11-
label: '选择 Docx 文件预览效果(不会上传到服务器)',
11+
label: '选择 Docx/Excel 文件预览效果(不会上传到服务器)',
1212
asBlob: true,
13-
accept: '.docx'
13+
accept: '.docx,.xlsx,.csv,.tsv'
1414
},
1515
{
1616
type: 'office-viewer',
1717
id: 'office-viewer',
18-
name: 'file'
18+
name: 'file',
19+
excelOptions: {
20+
height: 500
21+
}
1922
}
2023
]
2124
}

examples/static/all.xlsx

266 KB
Binary file not shown.

fis-conf.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
526526
'!punycode/**',
527527
'!office-viewer/**',
528528
'!fflate/**',
529+
'!numfmt/**',
529530
'!amis-formula/lib/doc.js'
530531
],
531532

@@ -579,7 +580,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
579580
'echarts-wordcloud/**'
580581
],
581582

582-
'office-viewer.js': ['office-viewer/**', 'fflate/**'],
583+
'office-viewer.js': ['office-viewer/**', 'fflate/**', 'numfmt/**'],
583584
'json-view.js': 'react-json-view/**',
584585
'fomula-doc.js': 'amis-formula/lib/doc.js',
585586

@@ -608,7 +609,8 @@ if (fis.project.currentMedia() === 'publish-sdk') {
608609
'!markdown-it/**',
609610
'!markdown-it-html5-media/**',
610611
'!office-viewer/**',
611-
'!fflate/**'
612+
'!fflate/**',
613+
'!numfmt/**'
612614
]
613615
}),
614616
postpackager: [
@@ -842,6 +844,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
842844
'!punycode/**',
843845
'!amis-formula/**',
844846
'!fflate/**',
847+
'!numfmt/**',
845848
'!office-viewer/**',
846849
'!amis-core/**',
847850
'!amis-ui/**',
@@ -908,7 +911,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
908911
'!/examples/components/EChartsEditor/Common.tsx'
909912
],
910913

911-
'pkg/office-viewer.js': ['office-viewer/**', 'fflate/**'],
914+
'pkg/office-viewer.js': ['office-viewer/**', 'fflate/**', 'numfmt/**'],
912915

913916
'pkg/rest.js': [
914917
'**.{js,jsx,ts,tsx}',
@@ -933,7 +936,8 @@ if (fis.project.currentMedia() === 'publish-sdk') {
933936
'!uc.micro/**',
934937
'!markdown-it/**',
935938
'!markdown-it-html5-media/**',
936-
'!fflate/**'
939+
'!fflate/**',
940+
'!numfmt/**'
937941
],
938942

939943
'pkg/npm.css': ['node_modules/*/**.css', '!monaco-editor/**', '!amis/**'],

index.html

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/>
2727
<link rel="stylesheet" href="/node_modules/katex/dist/katex.min.css" />
2828
<link rel="stylesheet" href="/node_modules/prismjs/themes/prism.css" />
29+
<link rel="stylesheet" href="/node_modules/office-viewer/dist/office.css" />
2930
<link rel="stylesheet" href="/examples/doc.css" />
3031

3132
<link rel="stylesheet" href="/examples/style.scss" />

packages/amis/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"moment": "^2.19.4",
6363
"moment-timezone": "^0.5.34",
6464
"mpegts.js": "^1.6.10",
65-
"office-viewer": "^0.2.1",
65+
"office-viewer": "^0.3.5",
6666
"prop-types": "^15.6.1",
6767
"qrcode.react": "^3.1.0",
6868
"react-cropper": "^2.1.8",
@@ -244,4 +244,4 @@
244244
"react-dom": ">=16.8.6"
245245
},
246246
"gitHead": "37d23b4a8eb1c663bc38e8dd9040889ea1526ec4"
247-
}
247+
}

packages/amis/src/renderers/OfficeViewer.tsx

+59-21
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
resolveVariableAndFilter,
1717
ScopedContext
1818
} from 'amis-core';
19-
import type {Word} from 'office-viewer';
19+
import type {Word, Excel} from 'office-viewer';
2020
import {Spinner} from 'amis-ui';
2121
import {Payload} from '../types';
2222

@@ -55,13 +55,15 @@ export default class OfficeViewer extends React.Component<
5555
> {
5656
rootElement: React.RefObject<HTMLDivElement>;
5757

58-
word: Word;
58+
office: Word | Excel;
5959

6060
fileName?: string;
6161

6262
// 文档数据,避免 update 参数的时候重复加载
6363
document?: any;
6464

65+
finalSrc?: string;
66+
6567
constructor(props: OfficeViewerProps) {
6668
super(props);
6769
this.rootElement = React.createRef();
@@ -112,7 +114,7 @@ export default class OfficeViewer extends React.Component<
112114
this.renderWord();
113115
} else {
114116
// 默认只更新变量提升性能
115-
this.word?.updateVariable();
117+
this.office?.updateVariable();
116118
}
117119
}
118120
}
@@ -124,11 +126,11 @@ export default class OfficeViewer extends React.Component<
124126
const actionType = action?.actionType as string;
125127

126128
if (actionType === 'saveAs') {
127-
this.word?.download(args?.name || this.fileName);
129+
this.office?.download(args?.name || this.fileName);
128130
}
129131

130132
if (actionType === 'print') {
131-
this.word?.print();
133+
this.office?.print();
132134
}
133135
}
134136

@@ -171,6 +173,9 @@ export default class OfficeViewer extends React.Component<
171173
console.warn('file src is empty');
172174
return;
173175
}
176+
177+
this.finalSrc = finalSrc;
178+
174179
let response: Payload;
175180

176181
this.setState({
@@ -195,32 +200,69 @@ export default class OfficeViewer extends React.Component<
195200
}
196201
}
197202

203+
async initOffice(officeViewer: any, file?: ArrayBuffer) {
204+
const {
205+
wordOptions,
206+
excelOptions,
207+
env,
208+
src,
209+
data,
210+
translate: __
211+
} = this.props;
212+
const createOfficeViewer = officeViewer.createOfficeViewer;
213+
const office = await createOfficeViewer(
214+
file || this.document,
215+
{},
216+
this.finalSrc
217+
);
218+
219+
if (office instanceof officeViewer.Word) {
220+
office.updateOptions({
221+
...wordOptions,
222+
data,
223+
evalVar: this.evalVar.bind(this)
224+
});
225+
} else if (office instanceof officeViewer.Excel) {
226+
office.updateOptions({
227+
...excelOptions,
228+
data,
229+
evalVar: this.evalVar.bind(this)
230+
});
231+
await office.loadExcel();
232+
}
233+
234+
return office;
235+
}
236+
198237
/**
199238
* 渲染远端文件
200239
*/
201240
async renderRemoteWord() {
202-
const {wordOptions, env, src, data, display, translate: __} = this.props;
241+
const {
242+
wordOptions,
243+
excelOptions,
244+
env,
245+
src,
246+
data,
247+
display,
248+
translate: __
249+
} = this.props;
203250

204251
if (!this.document) {
205252
return;
206253
}
207254

208255
import('office-viewer').then(async (officeViewer: any) => {
209-
const Word = officeViewer.Word;
210-
const word = new Word(this.document, {
211-
...wordOptions,
212-
data,
213-
evalVar: this.evalVar.bind(this)
214-
});
256+
const office = await this.initOffice(officeViewer);
215257

216258
if (display !== false) {
217-
word.render(this.rootElement?.current!);
259+
office.render(this.rootElement?.current!);
218260
} else if (display === false && this.rootElement?.current) {
219261
// 设置为 false 后清空
220262
this.rootElement.current.innerHTML = '';
221263
}
222264

223-
this.word = word;
265+
this.office = office;
224266
});
225267
}
226268

@@ -236,18 +278,14 @@ export default class OfficeViewer extends React.Component<
236278
const data = reader.result as ArrayBuffer;
237279

238280
import('office-viewer').then(async (officeViewer: any) => {
239-
const Word = officeViewer.Word;
240-
const word = new Word(data, {
241-
...wordOptions,
242-
evalVar: this.evalVar.bind(this)
243-
});
281+
const office = await this.initOffice(officeViewer, data);
244282
if (display !== false) {
245-
word.render(this.rootElement?.current!);
283+
office.render(this.rootElement?.current!);
246284
} else if (display === false && this.rootElement?.current) {
247285
// 设置为 false 后清空
248286
this.rootElement.current.innerHTML = '';
249287
}
250-
this.word = word;
288+
this.office = office;
251289
});
252290
};
253291
reader.readAsArrayBuffer(file);

packages/office-viewer/.gitignore

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
/coverage
2-
/lib
3-
/esm
1+
coverage
2+
lib
3+
esm
4+
.rollup.cache
5+
~$*

0 commit comments

Comments
 (0)