mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
修复 ghpages 没有 xlsx 问题 (#9836)
This commit is contained in:
parent
6be680149d
commit
e8bd1013f0
@ -92,6 +92,7 @@ fis.set('project.files', [
|
||||
'/examples/static/*.jpg',
|
||||
'/examples/static/*.jpeg',
|
||||
'/examples/static/*.docx',
|
||||
'/examples/static/*.xlsx',
|
||||
'/examples/static/photo/*.jpeg',
|
||||
'/examples/static/photo/*.png',
|
||||
'/examples/static/audio/*.mp3',
|
||||
@ -699,7 +700,8 @@ if (fis.project.currentMedia() === 'publish-sdk') {
|
||||
const ghPages = fis.media('gh-pages');
|
||||
ghPages.set('project.files', [
|
||||
'examples/index.html',
|
||||
'/examples/static/*.docx'
|
||||
'/examples/static/*.docx',
|
||||
'/examples/static/*.xlsx'
|
||||
]);
|
||||
|
||||
ghPages.match('*.scss', {
|
||||
@ -1022,7 +1024,7 @@ if (fis.project.currentMedia() === 'publish-sdk') {
|
||||
useHash: true
|
||||
});
|
||||
|
||||
ghPages.match('*.docx', {
|
||||
ghPages.match('*.{docx,xlsx}', {
|
||||
useHash: false
|
||||
});
|
||||
|
||||
|
3
packages/office-viewer/__tests__/happydom.ts
Normal file
3
packages/office-viewer/__tests__/happydom.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import {GlobalRegistrator} from '@happy-dom/global-registrator';
|
||||
|
||||
GlobalRegistrator.register();
|
@ -16,7 +16,7 @@ export function createWord(fileName: string, data: any) {
|
||||
|
||||
export async function snapShotTest(filePath: string) {
|
||||
// jsdom 不支持这个函数
|
||||
global.URL.createObjectURL = jest.fn(x => 'blob:http://localhost/mock');
|
||||
global.URL.createObjectURL = () => 'blob:http://localhost/mock';
|
||||
document.body.innerHTML = `
|
||||
<div id="root"></div>
|
||||
`;
|
||||
|
2
packages/office-viewer/bunfig.toml
Normal file
2
packages/office-viewer/bunfig.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[test]
|
||||
preload = "./__tests__/happydom.ts"
|
@ -65,6 +65,7 @@
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@happy-dom/global-registrator": "^14.2.0",
|
||||
"@rollup/plugin-commonjs": "^22.0.2",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
@ -115,4 +116,4 @@
|
||||
"printBasicPrototype": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,20 @@ import {ANY_KEY, Attributes} from '../openxml/Attributes';
|
||||
import {normalizeBoolean} from '../OpenXML';
|
||||
import {XMLNode} from '../util/xml';
|
||||
|
||||
const removeNameSpace = new RegExp('a:|xdr:|c:');
|
||||
|
||||
const replaceCache: Map<string, string> = new Map();
|
||||
|
||||
/**
|
||||
* 目前不支持这两种 name space
|
||||
*/
|
||||
function removeNamespace(tag: string) {
|
||||
return tag.replace('a:', '').replace('xdr:', '').replace('c:', '');
|
||||
if (replaceCache.has(tag)) {
|
||||
return replaceCache.get(tag)!;
|
||||
}
|
||||
const result = tag.replace('a:', '').replace('xdr:', '').replace('c:', '');
|
||||
replaceCache.set(tag, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,12 @@
|
||||
import {onClickOutside} from '../../../util/onClickOutside';
|
||||
import {onClickOutsideOnce} from '../../../util/onClickOutsideOnce';
|
||||
import {Workbook} from '../../Workbook';
|
||||
import {HitTestResult} from '../../render/selection/hitTest';
|
||||
import {Input} from '../../render/ui/Input';
|
||||
import {Sheet} from '../../sheet/Sheet';
|
||||
import {CellData, updateValue} from '../../types/worksheet/CellData';
|
||||
|
||||
let lastCellEditor: CellEditor | undefined;
|
||||
|
||||
/**
|
||||
* 单元格编辑
|
||||
*/
|
||||
@ -30,11 +32,16 @@ export class CellEditor {
|
||||
|
||||
col: number;
|
||||
|
||||
removeOnClickOutside: () => void;
|
||||
|
||||
constructor(
|
||||
dataContainer: HTMLElement,
|
||||
workbook: Workbook,
|
||||
hitTest: HitTestResult
|
||||
) {
|
||||
if (lastCellEditor) {
|
||||
lastCellEditor.close();
|
||||
}
|
||||
this.workbook = workbook;
|
||||
this.editorContainer = document.createElement('div');
|
||||
this.editorContainer.className = 'excel-cell-editor';
|
||||
@ -72,15 +79,17 @@ export class CellEditor {
|
||||
this.initValue = cellInfo.value;
|
||||
this.value = cellInfo.value;
|
||||
|
||||
const input = new Input(
|
||||
this.editorContainer,
|
||||
'',
|
||||
cellInfo.value,
|
||||
value => {
|
||||
const input = new Input({
|
||||
container: this.editorContainer,
|
||||
value: cellInfo.value,
|
||||
onChange: value => {
|
||||
this.handleInput(value);
|
||||
},
|
||||
'borderLess'
|
||||
);
|
||||
onEnter: value => {
|
||||
this.close();
|
||||
},
|
||||
style: 'borderLess'
|
||||
});
|
||||
|
||||
input.force();
|
||||
|
||||
@ -89,9 +98,11 @@ export class CellEditor {
|
||||
this.editorContainer.style.width = `${width}px`;
|
||||
this.editorContainer.style.height = `${height}px`;
|
||||
|
||||
onClickOutside(this.editorContainer, () => {
|
||||
onClickOutsideOnce(this.editorContainer, () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
lastCellEditor = this;
|
||||
}
|
||||
|
||||
handleInput(value: string) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {xml2json} from '../../../util/xml';
|
||||
import {parseXML, xml2json} from '../../../util/xml';
|
||||
import {IWorksheet} from '../../types/IWorksheet';
|
||||
|
||||
import {StringItem} from '../../types/StringItem';
|
||||
@ -24,6 +24,7 @@ import {initValueForContainsBlanks} from './initValueForContainsBlanks';
|
||||
import {parseTableParts} from './parseTableParts';
|
||||
import {initValueForTable} from './initValueForTable';
|
||||
import {IWorkbook} from '../../types/IWorkbook';
|
||||
import {xmlToNode} from '../../../util/xmlToNode';
|
||||
|
||||
/**
|
||||
* 解析 xl/worksheets/sheet*.xml 文件
|
||||
@ -39,7 +40,9 @@ export async function parseWorksheet(
|
||||
if (!xml) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const node = await xml2json(xml);
|
||||
|
||||
const worksheet: IWorksheet = {
|
||||
cols: [],
|
||||
rows: [],
|
||||
|
@ -7,8 +7,8 @@ import {autoWrapText} from '../cell/autoWrapText';
|
||||
* canvas 在不同操作系统下表现不一致,所以这里 mock 一下来保证测试的稳定性
|
||||
*/
|
||||
const mockCtx = {
|
||||
save: jest.fn(),
|
||||
restore: jest.fn(),
|
||||
save: () => {},
|
||||
restore: () => {},
|
||||
measureText: (text: string) => {
|
||||
const width = stringToArray(text).length * 10;
|
||||
return {
|
||||
|
@ -32,6 +32,8 @@ export class AutoFilterIconUI {
|
||||
|
||||
autoFilter: CT_AutoFilter;
|
||||
|
||||
removeClickOutsideEvent: () => void;
|
||||
|
||||
constructor(
|
||||
sheet: Sheet,
|
||||
dataContainer: HTMLElement,
|
||||
@ -72,7 +74,7 @@ export class AutoFilterIconUI {
|
||||
);
|
||||
|
||||
filterIcon.addEventListener('click', this.handleClick.bind(this));
|
||||
onClickOutside(filterIconContainer, () => {
|
||||
this.removeClickOutsideEvent = onClickOutside(filterIconContainer, () => {
|
||||
this.hideMenu();
|
||||
});
|
||||
}
|
||||
@ -136,4 +138,9 @@ export class AutoFilterIconUI {
|
||||
hide() {
|
||||
this.filterIconContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.filterIconContainer.remove();
|
||||
this.removeClickOutsideEvent();
|
||||
}
|
||||
}
|
||||
|
@ -173,16 +173,15 @@ export class CustomFiltersUI {
|
||||
}
|
||||
);
|
||||
|
||||
const input = new Input(
|
||||
customFilterItemInput,
|
||||
'',
|
||||
const input = new Input({
|
||||
container: customFilterItemInput,
|
||||
value,
|
||||
() => {
|
||||
onChange: () => {
|
||||
this.syncCustomFilters();
|
||||
},
|
||||
'normal',
|
||||
this.texts
|
||||
);
|
||||
style: 'normal',
|
||||
options: this.texts
|
||||
});
|
||||
|
||||
this.customFilterItems.push({input, select});
|
||||
}
|
||||
|
@ -63,15 +63,13 @@ export class FormulaBar {
|
||||
});
|
||||
this.textBox = textBox;
|
||||
|
||||
const textInput = new Input(
|
||||
textBox,
|
||||
'',
|
||||
'',
|
||||
value => {
|
||||
const textInput = new Input({
|
||||
container: textBox,
|
||||
onChange: value => {
|
||||
this.changeCellValue(value);
|
||||
},
|
||||
'borderLess'
|
||||
);
|
||||
style: 'borderLess'
|
||||
});
|
||||
this.textInput = textInput;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,9 @@ export function drawRowColHeaders(
|
||||
defaultFontSize: FontSize,
|
||||
defaultFontStyle: FontStyle
|
||||
) {
|
||||
if (currentSheet.showRowColHeaders() === false) {
|
||||
return;
|
||||
}
|
||||
const {rows, startRowOffset, height, width, cols, startColOffset} = viewRange;
|
||||
|
||||
const {rowHeaderWidth, colHeaderHeight} = currentSheet.getRowColSize();
|
||||
|
@ -34,14 +34,13 @@ export class CheckBoxList {
|
||||
parent: container
|
||||
});
|
||||
|
||||
const searchInput = new Input(
|
||||
wrapper,
|
||||
searchPlaceholder,
|
||||
'',
|
||||
(text: string) => {
|
||||
const searchInput = new Input({
|
||||
container: wrapper,
|
||||
placeholder: searchPlaceholder,
|
||||
onChange: text => {
|
||||
this.handleSearch(text);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
this.searchInput = searchInput;
|
||||
|
||||
|
@ -7,31 +7,37 @@ let inputId = 0;
|
||||
export class Input {
|
||||
input: HTMLInputElement;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
placeholder: string,
|
||||
value: string,
|
||||
onChange: (value: string) => void,
|
||||
style: 'normal' | 'borderLess' = 'normal',
|
||||
options: string[] = []
|
||||
) {
|
||||
constructor(args: {
|
||||
container: HTMLElement;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
onEnter?: (value: string) => void;
|
||||
style?: 'normal' | 'borderLess';
|
||||
options?: string[];
|
||||
}) {
|
||||
this.input = document.createElement('input');
|
||||
this.input.value = value;
|
||||
this.input.placeholder = placeholder;
|
||||
container.appendChild(this.input);
|
||||
this.input.value = args.value || '';
|
||||
this.input.placeholder = args.placeholder || '';
|
||||
args.container.appendChild(this.input);
|
||||
this.input.className = 'excel-input';
|
||||
if (style === 'borderLess') {
|
||||
if (args.style === 'borderLess') {
|
||||
this.input.classList.add('excel-input-border-less');
|
||||
}
|
||||
this.input.oninput = () => {
|
||||
onChange(this.input.value);
|
||||
args.onChange(this.input.value);
|
||||
};
|
||||
this.input.onkeydown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
args.onEnter?.(this.input.value);
|
||||
}
|
||||
};
|
||||
|
||||
if (options.length) {
|
||||
if (args.options && args.options.length) {
|
||||
const datalist = document.createElement('datalist');
|
||||
datalist.id = `${inputId++}-list`;
|
||||
container.appendChild(datalist);
|
||||
options.forEach(option => {
|
||||
args.container.appendChild(datalist);
|
||||
args.options.forEach(option => {
|
||||
const optionElement = document.createElement('option');
|
||||
optionElement.value = option;
|
||||
datalist.appendChild(optionElement);
|
||||
|
@ -92,6 +92,17 @@ export function updateValue(value: string = '', cellData?: CellData): CellData {
|
||||
return value;
|
||||
}
|
||||
|
||||
if ('type' in cellData && cellData.type === 'blank') {
|
||||
if (cellData.s !== undefined) {
|
||||
return {
|
||||
type: 'style',
|
||||
value,
|
||||
s: cellData.s
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if ('value' in cellData) {
|
||||
return {
|
||||
...cellData,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import Word from '../../Word';
|
||||
import {Word} from '../../index';
|
||||
import {replaceVar} from '../replaceVar';
|
||||
import {buildXML} from '../xml';
|
||||
import {mergeRun} from '../mergeRun';
|
||||
|
178
packages/office-viewer/src/util/__tests__/xmlToNode.test.ts
Normal file
178
packages/office-viewer/src/util/__tests__/xmlToNode.test.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import {xmlToNode} from '../xmlToNode';
|
||||
|
||||
test('simple', () => {
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<row r="2" spans="1:3">
|
||||
<c r="A2">
|
||||
<f t="array" ref="A2">SUM(B1:C1*B2:C2)</f>
|
||||
<v>0</v>
|
||||
</c>
|
||||
<c r="B2">
|
||||
<v>10</v>
|
||||
</c>
|
||||
<c r="C2">
|
||||
<v>15</v>
|
||||
</c>
|
||||
</row>`;
|
||||
|
||||
const node = xmlToNode(xml);
|
||||
|
||||
expect(node).toStrictEqual({
|
||||
tag: 'row',
|
||||
attrs: {
|
||||
r: '2',
|
||||
spans: '1:3'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
tag: 'c',
|
||||
attrs: {
|
||||
r: 'A2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
tag: 'f',
|
||||
attrs: {
|
||||
t: 'array',
|
||||
ref: 'A2'
|
||||
},
|
||||
children: [],
|
||||
text: 'SUM(B1:C1*B2:C2)'
|
||||
},
|
||||
{
|
||||
tag: 'v',
|
||||
attrs: {},
|
||||
children: [],
|
||||
text: '0'
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
},
|
||||
{
|
||||
tag: 'c',
|
||||
attrs: {
|
||||
r: 'B2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
tag: 'v',
|
||||
attrs: {},
|
||||
children: [],
|
||||
text: '10'
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
},
|
||||
{
|
||||
tag: 'c',
|
||||
attrs: {
|
||||
r: 'C2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
tag: 'v',
|
||||
attrs: {},
|
||||
children: [],
|
||||
text: '15'
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
});
|
||||
});
|
||||
|
||||
test('sst', () => {
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="22" uniqueCount="22">
|
||||
<si>
|
||||
<r>
|
||||
<rPr>
|
||||
<sz val="12"/>
|
||||
<color rgb="FFFF0000"/>
|
||||
<rFont val="等线"/>
|
||||
<family val="4"/>
|
||||
<charset val="134"/>
|
||||
</rPr>
|
||||
<t>rich</t>
|
||||
</r>
|
||||
</si>
|
||||
</sst>`;
|
||||
|
||||
const node = xmlToNode(xml);
|
||||
|
||||
expect(node).toStrictEqual({
|
||||
tag: 'sst',
|
||||
attrs: {
|
||||
xmlns: 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
|
||||
count: '22',
|
||||
uniqueCount: '22'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
tag: 'si',
|
||||
attrs: {},
|
||||
children: [
|
||||
{
|
||||
tag: 'r',
|
||||
attrs: {},
|
||||
children: [
|
||||
{
|
||||
tag: 'rPr',
|
||||
attrs: {},
|
||||
children: [
|
||||
{
|
||||
tag: 'sz',
|
||||
attrs: {
|
||||
val: '12'
|
||||
},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
tag: 'color',
|
||||
attrs: {
|
||||
rgb: 'FFFF0000'
|
||||
},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
tag: 'rFont',
|
||||
attrs: {
|
||||
val: '等线'
|
||||
},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
tag: 'family',
|
||||
attrs: {
|
||||
val: '4'
|
||||
},
|
||||
children: []
|
||||
},
|
||||
{
|
||||
tag: 'charset',
|
||||
attrs: {
|
||||
val: '134'
|
||||
},
|
||||
children: []
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
},
|
||||
{
|
||||
tag: 't',
|
||||
attrs: {},
|
||||
children: [],
|
||||
text: 'rich'
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
}
|
||||
],
|
||||
text: ''
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* 点击元素外部时触发回调
|
||||
* 点击元素外部时触发回调,用于一直存在的对象,需要手动调用删除监听器
|
||||
*/
|
||||
export function onClickOutside(
|
||||
element: HTMLElement,
|
||||
@ -12,4 +12,8 @@ export function onClickOutside(
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', outsideClickListener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', outsideClickListener);
|
||||
};
|
||||
}
|
||||
|
17
packages/office-viewer/src/util/onClickOutsideOnce.ts
Normal file
17
packages/office-viewer/src/util/onClickOutsideOnce.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 点击元素外部时触发回调,只触发一次,用于临时对象
|
||||
*/
|
||||
|
||||
export function onClickOutsideOnce(
|
||||
element: HTMLElement,
|
||||
onClickOutside: () => void
|
||||
) {
|
||||
const outsideClickListener = (event: MouseEvent) => {
|
||||
if (event.target instanceof Node && !element.contains(event.target)) {
|
||||
onClickOutside();
|
||||
document.removeEventListener('mousedown', outsideClickListener);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', outsideClickListener);
|
||||
}
|
144
packages/office-viewer/src/util/xmlToNode.ts
Normal file
144
packages/office-viewer/src/util/xmlToNode.ts
Normal file
@ -0,0 +1,144 @@
|
||||
import {XMLNode} from './xml';
|
||||
|
||||
const openBracket = '<';
|
||||
const closeBracket = '>';
|
||||
const slash = '/';
|
||||
const space = ' ';
|
||||
const questionMark = '?';
|
||||
|
||||
/**
|
||||
* 简单 XML 解析,目前看和 SaxesParser 比快不了多少,10 万行快了 1 秒,因为实现可能不完全准确,目前先不用
|
||||
*/
|
||||
export function xmlToNode(xml: string): XMLNode {
|
||||
let position = 0;
|
||||
|
||||
// 当前 文本
|
||||
let text = '';
|
||||
let xmlLength = xml.length;
|
||||
|
||||
const nodeStack: XMLNode[] = [];
|
||||
|
||||
// 处理属性
|
||||
function processAttr() {
|
||||
const currentNode = nodeStack[nodeStack.length - 1];
|
||||
let attrName = '';
|
||||
while (position < xmlLength) {
|
||||
const char = xml[position];
|
||||
if (char === ' ') {
|
||||
position++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 属性结束
|
||||
if (char === '=') {
|
||||
// ' 或 ",目前要求 = 号后面一定是引号
|
||||
const quote = xml[position + 1];
|
||||
const endQuote = xml.indexOf(quote, position + 2);
|
||||
const attrValue = xml
|
||||
.substring(position + 2, endQuote)
|
||||
.replace(/"/g, '"');
|
||||
currentNode.attrs[attrName] = attrValue;
|
||||
attrName = '';
|
||||
position = endQuote + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '/' && xml[position + 1] === '>') {
|
||||
position = position + 2;
|
||||
if (nodeStack.length > 1) {
|
||||
nodeStack.pop();
|
||||
}
|
||||
text = '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (char === closeBracket) {
|
||||
position++;
|
||||
return;
|
||||
}
|
||||
|
||||
attrName += char;
|
||||
position++;
|
||||
}
|
||||
}
|
||||
|
||||
while (position < xmlLength) {
|
||||
const char = xml[position];
|
||||
// 忽略开始 xml 定义
|
||||
if (char === openBracket && xml[position + 1] === questionMark) {
|
||||
const end = xml.indexOf('?>', position);
|
||||
position = end + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 开始 tag
|
||||
if (char === openBracket) {
|
||||
// </结束标签
|
||||
if (xml[position + 1] === slash) {
|
||||
const currentNode = nodeStack[nodeStack.length - 1];
|
||||
if (!currentNode) {
|
||||
position = position + 2;
|
||||
// 只有结束节点,解析有问题
|
||||
console.error('xml parse error');
|
||||
continue;
|
||||
}
|
||||
currentNode.text = text
|
||||
.trim()
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<');
|
||||
if (nodeStack.length > 1) {
|
||||
nodeStack.pop();
|
||||
}
|
||||
const end = xml.indexOf(closeBracket, position);
|
||||
position = end + 1;
|
||||
text = '';
|
||||
continue;
|
||||
} else {
|
||||
// < 开始,找到第一个空格或者 > 结束
|
||||
let tagName = '';
|
||||
position = position + 1;
|
||||
while (position < xmlLength) {
|
||||
const char = xml[position];
|
||||
if (char === space || char === closeBracket) {
|
||||
break;
|
||||
}
|
||||
tagName += char;
|
||||
position++;
|
||||
}
|
||||
|
||||
const newNode = {
|
||||
tag: tagName,
|
||||
attrs: {},
|
||||
children: []
|
||||
};
|
||||
|
||||
const parent = nodeStack[nodeStack.length - 1];
|
||||
if (parent) {
|
||||
parent.children.push(newNode);
|
||||
}
|
||||
|
||||
nodeStack.push(newNode);
|
||||
|
||||
// 处理属性
|
||||
processAttr();
|
||||
|
||||
text = '';
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
text += char;
|
||||
|
||||
position++;
|
||||
|
||||
// 每隔 1024 截断一下,可能性能会更好?
|
||||
if (position > 124) {
|
||||
xml = xml.substring(position);
|
||||
position = 0;
|
||||
xmlLength = xml.length;
|
||||
}
|
||||
}
|
||||
|
||||
return nodeStack[0]!;
|
||||
}
|
Loading…
Reference in New Issue
Block a user