|
|
@@ -9,23 +9,51 @@
|
|
|
+ '<Author>{author}</Author>'
|
|
|
+ '<Created>{created}</Created>'
|
|
|
+'</DocumentProperties>'
|
|
|
- + '{styles}'
|
|
|
+ + '<Styles>{styles}</Styles>'
|
|
|
+ '{worksheets}'
|
|
|
+ '</Workbook>',
|
|
|
- tmpStyleXML = '<Styles>'
|
|
|
- + '<Style ss:ID="Bold"><Font ss:Bold="1"/></Style>'
|
|
|
- + '<Style ss:ID="Currency"><NumberFormat ss:Format="Currency"></NumberFormat></Style>'
|
|
|
- + '<Style ss:ID="Date"><NumberFormat ss:Format="yyyy-mm-dd"></NumberFormat></Style>'
|
|
|
- + '<Style ss:ID="Center"><Alignment ss:Horizontal="Center" ss:Vertical="Center"/></Style>'
|
|
|
- + '</Styles>',
|
|
|
tmplWorksheetXML = '<Worksheet ss:Name="{nameWS}"><Table>{columnWidth}{rows}</Table></Worksheet>',
|
|
|
tmplColumnWidthXML = '<Column ss:Width="{width}"/>',
|
|
|
- tmplTableHeaderXML = '<Row><Cell ss:StyleID="Center" ss:MergeAcross="{columnCount}"><Data ss:Type="String">{header}</Data></Cell></Row>',
|
|
|
+ tmplTableHeaderXML = '<Row><Cell ss:StyleID="{styleID}" ss:MergeAcross="{columnCount}"><Data ss:Type="String">{header}</Data></Cell></Row>',
|
|
|
tmplRowXML = '<Row>{cells}</Row>',
|
|
|
tmplCellXML = '<Cell{attributeStyleID}{attributeFormula}><Data ss:Type="{nameType}">{data}</Data></Cell>',
|
|
|
_defaultStyleIDs = {
|
|
|
- DateTime: 'Date',
|
|
|
+ DateTime: 'date',
|
|
|
},
|
|
|
+ tmplStyleXML = '<Style ss:ID="{styleID}">{styleNode}</Style>',
|
|
|
+ tmplStyleNodeXML = '<{nodeName} {attributes}></{nodeName}>',
|
|
|
+ tmplStyleNodeAttributeXML = 'ss:{key}="{value}"',
|
|
|
+ styleConfig = [{
|
|
|
+ name: "Date",
|
|
|
+ tag: "NumberFormat",
|
|
|
+ key: "Format",
|
|
|
+ value: "yyyy-mm-dd"
|
|
|
+ }, {
|
|
|
+ name: "bold",
|
|
|
+ tag: "Font",
|
|
|
+ key: "Bold",
|
|
|
+ value: "1"
|
|
|
+ }, {
|
|
|
+ name: "center",
|
|
|
+ tag: ["Alignment"],
|
|
|
+ key: [["Horizontal", "Vertical"]],
|
|
|
+ value: [["Center", "Center"]]
|
|
|
+ }, {
|
|
|
+ name: "header",
|
|
|
+ tag: ["Font", "Interior"],
|
|
|
+ key: ["Color", ["Color", "Pattern"]],
|
|
|
+ value: ["#FFFFFF", ["#70AD47", "Solid"]]
|
|
|
+ }, {
|
|
|
+ name: "even",
|
|
|
+ tag: ["Interior"],
|
|
|
+ key: [["Color", "Pattern"]],
|
|
|
+ value: [["#C6E0B4", "Solid"]]
|
|
|
+ }, {
|
|
|
+ name: "odd",
|
|
|
+ tag: ["Interior"],
|
|
|
+ key: [["Color", "Pattern"]],
|
|
|
+ value: [["#A9D08E", "Solid"]]
|
|
|
+ }],
|
|
|
_format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) },
|
|
|
_export = function(workbookXML, wbname) {
|
|
|
var eleLink = document.createElement('a');
|
|
|
@@ -47,18 +75,28 @@
|
|
|
this.json = json;
|
|
|
this.wbname = wbname;
|
|
|
this.workbookXML = '';
|
|
|
+ this.styleID = 1;
|
|
|
+ this.styles = [];
|
|
|
};
|
|
|
|
|
|
Exportor.prototype.generateWorkbookXML = function() {
|
|
|
let ctx = {
|
|
|
worksheets: this.generateWorksheetsXML(),
|
|
|
- styles: tmpStyleXML,
|
|
|
- author: 'zhuth',
|
|
|
- created: 'zhuth'
|
|
|
+ styles: this.generateStylesXML(),
|
|
|
+ author: 'usoftchina',
|
|
|
+ created: new Date().getTime()
|
|
|
};
|
|
|
return _format(tmplWorkbookXML, ctx);
|
|
|
};
|
|
|
|
|
|
+ Exportor.prototype.generateStylesXML = function() {
|
|
|
+ let str = '';
|
|
|
+ for(let i = 0; i < this.styles.length; i++) {
|
|
|
+ str += this.generateStyles(this.styles[i].styleID, this.styles[i].styleNames);
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ };
|
|
|
+
|
|
|
Exportor.prototype.generateWorksheetsXML = function() {
|
|
|
let str = '',
|
|
|
sheets = this.json.sheets;
|
|
|
@@ -85,7 +123,9 @@
|
|
|
Exportor.prototype.generateTableHeaderXML = function(header, columnCount) {
|
|
|
let str = '';
|
|
|
if(!!header) {
|
|
|
+ let styleID = this.addStyle(['center', 'header']);
|
|
|
let ctx = {
|
|
|
+ styleID: styleID,
|
|
|
columnCount: columnCount - 1,
|
|
|
header
|
|
|
}
|
|
|
@@ -106,7 +146,7 @@
|
|
|
};
|
|
|
|
|
|
Exportor.prototype.generateHeaderRowsXML = function(columns) {
|
|
|
- let cells = columns.map(c => ({ type: 'String', value: c.name, styleID: 'Bold' }));
|
|
|
+ let cells = columns.map(c => ({ type: 'String', value: c.name, styleNames: ['header', 'bold'] }));
|
|
|
let ctx = {
|
|
|
cells: this.generateCellsXML(cells)
|
|
|
};
|
|
|
@@ -115,16 +155,18 @@
|
|
|
};
|
|
|
|
|
|
Exportor.prototype.generateRowsXML = function(columns, rows) {
|
|
|
- let cols = columns.map(c => ({ type: c.type, name: c.name, styleID: c.styleID }));
|
|
|
+ let cols = columns.map(c => ({ type: c.type, name: c.name, styleNames: c.styleNames }));
|
|
|
let str = '';
|
|
|
for(let i = 0; i < rows.length; i++) {
|
|
|
let r = rows[i];
|
|
|
let cells = [];
|
|
|
+ let s = (i&1) === 0 ? 'even' : 'odd';
|
|
|
for(let j = 0; j < cols.length; j++) {
|
|
|
+ cols[j].styleNames = cols[j].styleNames && cols[j].styleNames.length>0 ? cols[j].styleNames: [];
|
|
|
cells.push({
|
|
|
type: cols[j].type,
|
|
|
value: r[j],
|
|
|
- styleID: cols[j].styleID || _defaultStyleIDs[cols[j].type]
|
|
|
+ styleNames: [s].concat(cols[j].styleNames.concat(_defaultStyleIDs[cols[j].type] ? [_defaultStyleIDs[cols[j].type]] : []))
|
|
|
});
|
|
|
}
|
|
|
let ctx = {
|
|
|
@@ -138,9 +180,10 @@
|
|
|
Exportor.prototype.generateCellsXML = function(cells) {
|
|
|
let str = '';
|
|
|
for(let i = 0; i < cells.length; i++) {
|
|
|
+ let styleID = this.addStyle(cells[i].styleNames);
|
|
|
let ctx = {
|
|
|
- attributeStyleID: cells[i].styleID ? _format(' ss:StyleID="{styleID}"', {
|
|
|
- styleID: cells[i].styleID
|
|
|
+ attributeStyleID: cells[i].styleNames && cells[i].styleNames.length > 0 ? _format(' ss:StyleID="{styleID}"', {
|
|
|
+ styleID: styleID
|
|
|
}) : '',
|
|
|
attributeFormula: '',
|
|
|
nameType: cells[i].type || 'String',
|
|
|
@@ -151,10 +194,97 @@
|
|
|
return str;
|
|
|
};
|
|
|
|
|
|
+ Exportor.prototype.addStyle = function(styleNames) {
|
|
|
+ if(!styleNames || styleNames.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let s1 = styleNames.sort();
|
|
|
+ let idx = -1; // 是否已存在
|
|
|
+ outer:
|
|
|
+ for(let i = 0; i < this.styles.length; i++) {
|
|
|
+ let style = this.styles[i];
|
|
|
+ let s2 = style.styleNames.sort();
|
|
|
+ if(s1.length !== s2.length) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for(let j = 0; j < s1.length; j++) {
|
|
|
+ if(s1[j] !== s2[j]) {
|
|
|
+ continue outer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ idx = i;
|
|
|
+ }
|
|
|
+ if(idx === -1) {
|
|
|
+ let styleID = 's' + this.styleID++
|
|
|
+ this.styles.push({
|
|
|
+ styleID: styleID,
|
|
|
+ styleNames: styleNames
|
|
|
+ });
|
|
|
+ return styleID;
|
|
|
+ }else {
|
|
|
+ return this.styles[idx].styleID;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ Exportor.prototype.generateStyles = function(styleID, styleNames) {
|
|
|
+ let configs = styleNames.map(name => styleConfig.find(s => s.name === name)).filter(c => !!c);
|
|
|
+ let str = '';
|
|
|
+ let obj = {};
|
|
|
+ for(let i = 0; i < configs.length; i++) {
|
|
|
+ let config = configs[i];
|
|
|
+ let tags = Object.prototype.toString.call(config.tag) === "[object Array]" ? config.tag : [config.tag];
|
|
|
+ let keyss = Object.prototype.toString.call(config.key) === "[object Array]" ? config.key : [config.key];
|
|
|
+ let valuess = Object.prototype.toString.call(config.value) === "[object Array]" ? config.value : [config.value];
|
|
|
+ for(let t = 0; t < tags.length; t++) {
|
|
|
+ let tag = tags[t];
|
|
|
+ let keys = Object.prototype.toString.call(keyss[t]) === "[object Array]" ? keyss[t] : [keyss[t]];
|
|
|
+ let values = Object.prototype.toString.call(valuess[t]) === "[object Array]" ? valuess[t] : [valuess[t]];
|
|
|
+ for(let k = 0; k < keys.length; k++) {
|
|
|
+ let key = keys[k];
|
|
|
+ let value = values[k];
|
|
|
+ obj[tag] = obj[tag] || {};
|
|
|
+ obj[tag][key] = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ str = this.generateStyle(styleID, obj);
|
|
|
+ return str;
|
|
|
+ };
|
|
|
+
|
|
|
+ Exportor.prototype.generateStyle = function(styleID, styleConfig) {
|
|
|
+ let str = '';
|
|
|
+ str = _format(tmplStyleXML, {
|
|
|
+ styleID: styleID,
|
|
|
+ styleNode: this.generateStyleNode(styleConfig)
|
|
|
+ })
|
|
|
+ return str;
|
|
|
+ };
|
|
|
+
|
|
|
+ Exportor.prototype.generateStyleNode = function(styleConfig) {
|
|
|
+ let str = '';
|
|
|
+ for(let k in styleConfig) {
|
|
|
+ str += _format(tmplStyleNodeXML, {
|
|
|
+ nodeName: k,
|
|
|
+ attributes: this.generateStyleNodeAttribute(styleConfig[k])
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ };
|
|
|
+
|
|
|
+ Exportor.prototype.generateStyleNodeAttribute = function(styleNodeConfig) {
|
|
|
+ let str = '';
|
|
|
+ for(let k in styleNodeConfig) {
|
|
|
+ str += _format(tmplStyleNodeAttributeXML, {
|
|
|
+ key: k,
|
|
|
+ value: styleNodeConfig[k]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ };
|
|
|
+
|
|
|
Exportor.prototype.export = function() {
|
|
|
- console.log(this.workbookXML);
|
|
|
_export(this.workbookXML, this.wbname);
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
return Exportor;
|
|
|
|