mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:39:05 +08:00
feat(ooxml): 支持艺术字部分样式;修复外部数据变更不会重新渲染问题 (#6702)
This commit is contained in:
parent
7428ea8620
commit
1692273925
@ -87,12 +87,12 @@ Word 渲染支持以下功能:
|
|||||||
分页渲染的其它设置项
|
分页渲染的其它设置项
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
| ------------------ | --------- | --------- | ------------------------------------------ | --- |
|
| ------------------ | --------- | --------- | ------------------------------------------ |
|
||||||
| page | `boolean` | false | 是否开启分页渲染 |
|
| page | `boolean` | false | 是否开启分页渲染 |
|
||||||
| pageMarginBottom | `number` | 20 | 页面上下间距 |
|
| pageMarginBottom | `number` | 20 | 页面上下间距 |
|
||||||
| pageBackground | `string` | '#FFF' | 页面内背景色 |
|
| pageBackground | `string` | '#FFF' | 页面内背景色 |
|
||||||
| pageShadow | `boolean` | true | 是否显示阴影 |
|
| pageShadow | `boolean` | true | 是否显示阴影 |
|
||||||
| pageWrap | `boolean` | true | 是否显示页面包裹 | |
|
| pageWrap | `boolean` | true | 是否显示页面包裹 |
|
||||||
| pageWrapBackground | `string` | '#ECECEC' | 页面包裹的背景色 |
|
| pageWrapBackground | `string` | '#ECECEC' | 页面包裹的背景色 |
|
||||||
| zoom | `number` | | 缩放比例,取值 0-1 之间 |
|
| zoom | `number` | | 缩放比例,取值 0-1 之间 |
|
||||||
| zoomFitWidth | `boolean` | false | 自适应宽度缩放,如果设置了 zoom 将不会生效 |
|
| zoomFitWidth | `boolean` | false | 自适应宽度缩放,如果设置了 zoom 将不会生效 |
|
||||||
@ -194,54 +194,51 @@ Word 渲染支持以下功能:
|
|||||||
```schema
|
```schema
|
||||||
{
|
{
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"data": {
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
name: 'u1',
|
|
||||||
age: 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'u2',
|
|
||||||
age: 11
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"body": [
|
"body": [
|
||||||
{
|
{
|
||||||
"type": "office-viewer",
|
"type": "office-viewer",
|
||||||
"src": "/examples/static/table-list.docx",
|
"id": "office-viewer-table-list",
|
||||||
"wordOptions": {
|
"src": "/examples/static/table-list.docx",
|
||||||
"padding": "8px"
|
"wordOptions": {
|
||||||
}
|
"padding": "8px"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "action",
|
|
||||||
"label": "下载文档",
|
|
||||||
"onEvent": {
|
|
||||||
"click": {
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"actionType": "saveAs",
|
|
||||||
"componentId": "office-viewer-table-list"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "office-viewer",
|
"type": "service",
|
||||||
"id": "office-viewer-table-list",
|
"api": "/api/mock2/sample/mirror?json=%7B%22users%22%3A%5B%7B%22name%22%3A%22u1%22%2C%22age%22%3A10%7D%2C%7B%22name%22%3A%22u2%22%2C%22age%22%3A11%7D%5D%7D",
|
||||||
"src": "/examples/static/table-list.docx",
|
"body": [{
|
||||||
"wordOptions": {
|
"type": "office-viewer",
|
||||||
"padding": "8px",
|
"src": "/examples/static/table-list.docx",
|
||||||
"enableVar": true,
|
"wordOptions": {
|
||||||
"ignoreWidth": true
|
"padding": "8px",
|
||||||
}
|
"enableVar": true,
|
||||||
}]
|
"ignoreWidth": true
|
||||||
|
},
|
||||||
|
"trackExpression": "${users}"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "action",
|
||||||
|
"label": "下载文档",
|
||||||
|
"onEvent": {
|
||||||
|
"click": {
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"actionType": "saveAs",
|
||||||
|
"componentId": "office-viewer-table-list"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
循环的语法是以 `{{#name}}` 开始,`{{/}}` 结束,在这期间的变量会取循环内的值
|
循环的语法是以 `{{#name}}` 开始,`{{/}}` 结束,在这期间的变量会取循环内的值。
|
||||||
|
|
||||||
|
注意上面的例子用到了 `trackExpression`,默认情况下如果设置了 `enableVar`,每次上层数据变化都会重新渲染文档,如果文档较大可能会有性能问题,这时可以通过配置 `trackExpression` 来限制只有某个数据变化时才重新渲染。
|
||||||
|
|
||||||
## 不渲染模式
|
## 不渲染模式
|
||||||
|
|
||||||
|
@ -263,12 +263,20 @@ function bulkUpdate2(req, res) {
|
|||||||
function mirror(req, res) {
|
function mirror(req, res) {
|
||||||
const json = JSON.parse(req.query.json);
|
const json = JSON.parse(req.query.json);
|
||||||
console.log('mirror', json);
|
console.log('mirror', json);
|
||||||
if ('status' in json) {
|
|
||||||
return res.json(json);
|
const response = () => {
|
||||||
} else {
|
if ('status' in json) {
|
||||||
return res.json({
|
return res.json(json);
|
||||||
status: 0,
|
} else {
|
||||||
data: json
|
return res.json({
|
||||||
});
|
status: 0,
|
||||||
|
data: json
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (req.query.waitSeconds) {
|
||||||
|
return setTimeout(response, parseInt(req.query.waitSeconds, 10) * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response();
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
"rollup-pluginutils": "^2.8.2",
|
"rollup-pluginutils": "^2.8.2",
|
||||||
"setprototypeof": "^1.2.0",
|
"setprototypeof": "^1.2.0",
|
||||||
"ts-jest": "^29.0.2",
|
"ts-jest": "^29.0.2",
|
||||||
"vite": "^4.2.1",
|
"vite": "^4.3.1",
|
||||||
"vite-plugin-monaco-editor": "^1.1.0",
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vite-plugin-svgr": "^2.2.2",
|
"vite-plugin-svgr": "^2.2.2",
|
||||||
"zrender": "^5.3.2"
|
"zrender": "^5.3.2"
|
||||||
|
@ -7,6 +7,7 @@ import {BaseSchema} from '../Schema';
|
|||||||
import {
|
import {
|
||||||
ActionObject,
|
ActionObject,
|
||||||
createObject,
|
createObject,
|
||||||
|
filter,
|
||||||
isApiOutdated,
|
isApiOutdated,
|
||||||
IScopedContext,
|
IScopedContext,
|
||||||
Renderer,
|
Renderer,
|
||||||
@ -28,7 +29,7 @@ export interface OfficeViewerSchema extends BaseSchema {
|
|||||||
/**
|
/**
|
||||||
* word 文档的渲染配置
|
* word 文档的渲染配置
|
||||||
*/
|
*/
|
||||||
wordOptions?: {};
|
wordOptions?: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示文档
|
* 是否显示文档
|
||||||
@ -86,8 +87,18 @@ export default class OfficeViewer extends React.Component<
|
|||||||
this.renderWord();
|
this.renderWord();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 这个变量替换只会更新变化的部分,所以性能还能接受
|
if (props.wordOptions?.enableVar) {
|
||||||
this.word?.updateVariable();
|
if (
|
||||||
|
props.trackExpression &&
|
||||||
|
filter(props.trackExpression, props.data) !==
|
||||||
|
filter(prevProps.trackExpression, prevProps.data)
|
||||||
|
) {
|
||||||
|
this.renderWord();
|
||||||
|
} else {
|
||||||
|
// 目前 word 渲染比较快,所以全量渲染性能可以接受
|
||||||
|
this.renderWord();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
File diff suppressed because it is too large
Load Diff
BIN
packages/ooxml-viewer/__tests__/docx/simple/loop-var-test.docx
Normal file
BIN
packages/ooxml-viewer/__tests__/docx/simple/loop-var-test.docx
Normal file
Binary file not shown.
BIN
packages/ooxml-viewer/__tests__/docx/simple/word-art.docx
Normal file
BIN
packages/ooxml-viewer/__tests__/docx/simple/word-art.docx
Normal file
Binary file not shown.
@ -61,7 +61,6 @@
|
|||||||
"ts-loader": "^9.2.3",
|
"ts-loader": "^9.2.3",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^4.3.5",
|
||||||
"vite": "^3.2.2",
|
|
||||||
"xml-formatter": "^3.3.2"
|
"xml-formatter": "^3.3.2"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
@ -66,10 +66,12 @@ export function parseChildColor(word: Word, element: Element): string {
|
|||||||
return srgbClr;
|
return srgbClr;
|
||||||
|
|
||||||
case 'a:schemeClr':
|
case 'a:schemeClr':
|
||||||
const schemeClr = colorChild.getAttribute('val') || '';
|
case 'w14:schemeClr':
|
||||||
|
const schemeClr = getVal(colorChild);
|
||||||
if (schemeClr) {
|
if (schemeClr) {
|
||||||
return changeLum(colorChild, word.getThemeColor(schemeClr));
|
return changeLum(colorChild, word.getThemeColor(schemeClr));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
|
@ -342,7 +342,17 @@ export function parsePr(word: Word, element: Element, type: 'r' | 'p' = 'p') {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w:rPr':
|
case 'w:rPr':
|
||||||
// TODO: 这个有时候会不正确,需要再看看
|
// TODO: 这个有时候和 r 里的 rPr 不一致,不知道如何处理
|
||||||
|
const reflection = child.getElementsByTagName('w14:reflection').item(0);
|
||||||
|
if (reflection) {
|
||||||
|
// css 只支持在块级节点设置
|
||||||
|
// 只支持一小部分设置项,另外因为只支持块级别的情况,所以看起来差异较大
|
||||||
|
const reflectionDistance =
|
||||||
|
parseSize(reflection, 'w4:dist', LengthUsage.Emu) || '0px';
|
||||||
|
style[
|
||||||
|
'-webkit-box-reflect'
|
||||||
|
] = `below ${reflectionDistance} linear-gradient(transparent, white)`;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w:rStyle':
|
case 'w:rStyle':
|
||||||
@ -429,26 +439,45 @@ export function parsePr(word: Word, element: Element, type: 'r' | 'p' = 'p') {
|
|||||||
|
|
||||||
case 'w:outline':
|
case 'w:outline':
|
||||||
style['text-shadow'] =
|
style['text-shadow'] =
|
||||||
'-1pt -1pt 0 #AAA, 1pt -1pt 0 #AAA, -1pt 1pt 0 #AAA, 1pt 1pt 0 #AAA';
|
'-1px -1px 0 #AAA, 1px -1px 0 #AAA, -1px 1px 0 #AAA, 1px 1px 0 #AAA';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w:shadown':
|
case 'w:shadown':
|
||||||
case 'w:imprint':
|
case 'w:imprint':
|
||||||
if (getValBoolean(child, true)) {
|
if (getValBoolean(child, true)) {
|
||||||
style['text-shadow'] = '1pt 1pt 2pt rgba(0, 0, 0, 0.6)';
|
style['text-shadow'] = '1px 1px 2px rgba(0, 0, 0, 0.6)';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w14:shadow':
|
case 'w14:shadow':
|
||||||
const blurRad =
|
const blurRad =
|
||||||
parseSize(child, 'w14:blurRad', LengthUsage.Emu) || '2pt';
|
parseSize(child, 'w14:blurRad', LengthUsage.Emu) || '4px';
|
||||||
// 其它结果算出来不像就先忽略了
|
// 其它结果算出来不像就先忽略了
|
||||||
let color = 'rgba(0, 0, 0, 0.6)';
|
let color = 'rgba(0, 0, 0, 0.6)';
|
||||||
const childColor = parseChildColor(word, child);
|
const childColor = parseChildColor(word, child);
|
||||||
if (childColor) {
|
if (childColor) {
|
||||||
color = childColor;
|
color = childColor;
|
||||||
}
|
}
|
||||||
style['text-shadow'] = `1pt 1pt ${blurRad} ${color}`;
|
style['text-shadow'] = `1px 1px ${blurRad} ${color}`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w14:textOutline':
|
||||||
|
const outlineWidth =
|
||||||
|
parseSize(child, 'w14:w', LengthUsage.Emu) || '1px';
|
||||||
|
|
||||||
|
style['-webkit-text-stroke-width'] = outlineWidth;
|
||||||
|
|
||||||
|
let outlineColor = 'white';
|
||||||
|
const fillColor = child.getElementsByTagName('w14:solidFill');
|
||||||
|
if (fillColor.length > 0) {
|
||||||
|
outlineColor = parseChildColor(word, fillColor.item(0)!) || 'white';
|
||||||
|
}
|
||||||
|
|
||||||
|
style['-webkit-text-stroke-color'] = outlineColor;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w14:reflection':
|
||||||
|
// 在 rPr 里处理了
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -30,6 +30,11 @@ function generateDefaultStyle(word: Word) {
|
|||||||
|
|
||||||
|
|
||||||
/** docDefaults **/
|
/** docDefaults **/
|
||||||
|
.${classPrefix} {
|
||||||
|
--docx-theme-font-minorHAnsi: Calibri, Helvetica, Arial, 'Helvetica Neue';
|
||||||
|
--docx-theme-font-minorEastAsia: 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'STHeiti',
|
||||||
|
'Microsoft YaHei';
|
||||||
|
}
|
||||||
|
|
||||||
.${classPrefix} p {
|
.${classPrefix} p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user