項目實戰下的經驗總結

背景

實現一個可以在wps、office的展示圖表、表格以及自定義頁眉、頁腳、字體大小以及圖片信息。
使用到的庫有 officegen 和 highchart-export-server

簡單介紹一下officegen

officegen 模塊可以爲Microsoft Office 2007及更高版本生成Office Open XML文件。此模塊不依賴於任何框架,您不需要安裝Microsoft Office,因此您可以將它用於任何類型的 JavaScript 應用程序。輸出也是流而不是文件,不依賴於任何輸出工具。officegen模塊應適用於支持任何支持Node的任何環境,包括Linux,OSX和Windows。

此模塊生成Excel(.xlsx),PowerPoint(.pptx)和Word(.docx)文檔。 Officegen還支持帶有嵌入數據的PowerPoint本機圖表對象。

使用officegen生成Word文件

const officegen = require('officegen')
const fs = require('fs')
// 創建一個空的word對象
let docx = officegen('docx') 

// 當word文件完成創建後會調用該函數
docx.on('finalize', written => {
  console.log('Finish to create a Microsoft Word document.')
})

// 創建一個段落,類似html裏面的p標籤
let pObj = docx.createP()

// 往標籤裏面添加內容
pObj.addText('Simple')

// 添加內容同事設置字體顏色,北京顏色
pObj.addText(' and back color.', { color: '00ffff', back: '000088' })

// 還可以往段落裏面添加圖片
pObj.addImage('some-image.png')

// 最後將docx對象塞到generate裏面生成名爲example.docx的文件
let out = fs.createWriteStream('example.docx')
docx.generate(out)

使用officegen生成PPT文件

const officegen = require('officegen')
let pptx = officegen('pptx')

// 創建一頁PPT,可以往裏面插圖表、文本、表格以及圖片
slide = pptx.makeNewSlide({
  userLayout: 'title'
});

// 設置標題跟設置段落一樣,可以設置字體大小/顏色,font-family
slide.setTitle([
  // This array is like a paragraph and you can use any settings that you pass for creating a paragraph,
  // Each object here is like a call to addText:
  {
    text: 'Hello ',
    options: {font_size: 56}
  },
  {
    text: 'World!',
    options: {
      font_size: 56,
      font_face: 'Arial',
      color: 'ffff00'
    }
  }
]);

// 插入圖表,這裏的chartInfo有一定的數據編排格式,建議如果產品線需要很多圖表的話,這裏可以單獨抽出來維護
slide.addChart( {   
    	 title: 'Column chart',
          renderType: 'column',  // 這裏設置圖表的樣式,類似echart裏面的type
          valAxisTitle: 'Costs/Revenues ($)',
          catAxisTitle: 'Category',
          valAxisNumFmt: '$0',
          valAxisMaxValue: 24,
    data:  [{
      name: 'Income',
      labels: ['2005', '2006', '2007', '2008', '2009'],
      values: [23.5, 26.2, 30.1, 29.5, 24.6],
      color: 'ff0000' 
    },
    {
      name: 'Expense',
      labels: ['2005', '2006', '2007', '2008', '2009'],
      values: [18.1, 22.8, 23.9, 25.1, 25],
      color: '00ff00' // optional
    }]
  })

// 創建一個table
let rows = [];
rows.push([
	{
		val: "Category",
        	opts: {
          		font_face   : "Arial",
          		align       : "l",
          		bold        : 0
        	}
      },
      {
        	val  :"Average Score",
        	opts: {
          		font_face   : "Arial",
          		align       : "r",
          		bold        : 1,
          		font_color  : "000000",
          		fill_color  : "f5f5f5"
        	}
      }
]);
slide.addTable(rows, {});

// 生成ppt
let out = fs.createWriteStream('example.pptx')
pptx.generate(out)

使用officegen生成Excel文件

const officegen = require('officegen')
const fs = require('fs')
let xlsx = officegen('xlsx')

let sheet = xlsx.makeNewSheet()
sheet.name = 'Officegen Excel'

// 往單元格里面插數據
sheet.setCell('E7', 42)

// 或者直接使用二維數組進行數據的插入
sheet.data[2][6] = 900
sheet.data[2][5] = 'more text'

let out = fs.createWriteStream('example.xlsx')
xlsx.generate(out)

以上就是officegen生成Word、Excel以及PPT的示例。如果你覺得本文已經結束了,那你太天真了

最近接到一個需求,要往word文檔裏面插入一個echart圖表(看清楚不是圖片),那就尷尬了,officegen沒有API支持,怎麼辦…焦頭爛耳中…兩小時的文檔查閱,發現一個突破點,就是pObj.addImage(‘some-image.png’),這個API,還記得嗎?如果我將echart圖生成圖片,然後以圖片的形式插進去起步就ok?

接下來就是考慮怎麼生成echart圖片的問題,接下來就是highchart-export-server的show-time。一款服務端的生成圖片的解決方案,而且只要你的系統支持Node它就能跑起來,這彷彿看到了曙光。

搭建highcharts-export-server

// 全局安裝highcharts-export-server
yarn global add highcarts-export-server

// 在highcharts-export-server安裝包的根目錄執行build.js
node build.js

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oR2wqg64-1583199017050)(./img/build.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FHGE1aSs-1583199017052)(./img/options.png)]

然後會彈出很多選項,根據自己的需求進行選擇

// 創建一個options.json配置文件,options.json 的內容爲
{"title":{"text":"不同城市的月平均氣溫","x":-20},"subtitle":{"text":"數據來源:WorldClimate.com","x":-20},"xAxis":{"categories":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},"yAxis":{"title":{"text":"溫度(°C)"},"plotLines":[{"value":0,"width":1,"color":"#808080"}]},"tooltip":{"valueSuffix":"°C"},"legend":{"layout":"vertical","align":"right","verticalAlign":"middle","borderWidth":0},"series":[{"name":"東京","data":[7,6.9,9.5,14.5,18.2,21.5,25.2,26.5,23.3,18.3,13.9,9.6]},{"name":"紐約","data":[-0.2,0.8,5.7,11.3,17,22,24.8,24.1,20.1,14.1,8.6,2.5]},{"name":"柏林","data":[-0.9,0.6,3.5,8.4,13.5,17,18.6,17.9,14.3,9,3.9,1]},{"name":"倫敦","data":[3.9,4.2,5.7,8.5,11.9,15.2,17,16.6,14.2,10.3,6.6,4.8]}]}

// 將圖表配置轉換成 PNG 圖片
highcharts-export-server --infile options.json --outfile chart.png

注意事項

// 一般發佈前的所有測試上線工作都是內網中進行,所以必須要把所有使用到的依賴和包都下載好放到本地
在開發環境一般是沒有網絡的,所以,提前將環境所需要的依賴、包都在有網的前提下佈置好,然後將那個包部署到對應的開發測試環境。對了,如果在linux環境下面輸出圖片,記得注意字體,因爲在window下面字體是微軟雅黑,然後在linux如果找不到就顯示不出對應的文字

// 把phantom放置到temp目錄下
C:\Users\youerName\AppData\Local\Temp\

// 當執行完node build.js的時候,會在當前目錄下生成phantom文件,裏面有一個export.html。注意裏面使用到了CDN
需要把對應的moment和timezone資源下載至本地,而且export.html需要在外網的環境下生成

翻譯映射表

在項目裏面,很多時候不僅僅需要簡體中文,還需要一些國外的翻譯,例如英語、韓語、繁體字等等,那麼怎樣做纔能有條不紊地,方便後人進行維護呢?

原理:以簡體中文爲key值,目標翻譯爲value,這樣每次新增一個國家的翻譯就可以通過創建多一個文件就行。考慮到翻譯工具整個項目都需要使用到,所以把_掛載到全局,然後記得在package.json裏面的eslintConfig配上"globals": {"_": true},不然eslint會報錯

# 想要達到的目標 
在`js`代碼中可以通過下劃線進行翻譯_('{0}你好{1}', '小明', new Date().getYear())
在模板中可以通過<lang :date="_('中國{0}', 2019)">你好{date}<lang>標籤和屬性翻譯
// index.js
const LANGUAGE = {
    'en_US': en // 這個en是一個翻譯文件
};

function _(str, ...args) {
    let langMap = LANGUAGE[cur];
    if (langMap && langMap.hasOwnProperty(str)) {
        str = langMap[str];
    }
    return str.replace(/\{(\d+|#\w+#)\}/g, function (m, i) {
        i = parseInt(i, 10);

        if (isNaN(i)) {
            return '';
        }

        if (i >= 0 && i < args.length) {
            return args[i];
        }
        return m;
    });
}

function initI18n () {
    if (typeof window !== 'undefined') {
        let old;
        if (window._) {
            old = window._;
            window._ = (str, ...args) => _(old.apply(null, arguments), ...args);
        } else {
            window._ = _;
        }
        return true;
    }
    return false;
}

initI18n();

// 內部實現一個install函數
export default {
    install (Vue, opt = { lang: 'zh_CN' }) {
        Vue.component('lang', Lang);  // 這裏面的Lang是一個組件
        Vue.prototype._ = _;
        cur = opt.lang;
    }
};

// lang.vue

    props: {
        showTitle: {
            type: Boolean,
            default: false
        }
    },

    methods: {
        _renderHTML () {
            let slotText = this.$slots.default[0].text;
            if (!this.$isMounted) {  // 注意這個isMounted這個是判斷是否已經mounted
                return slotText;
            }
            let attributeMap = Array.from(this.$el.attributes).reduce((prev, attr) => {
                prev[attr.nodeName] = attr.nodeValue;
                return prev;
            }, {});
            slotText = _(slotText);
            return slotText.replace(/\{(.+?)\}/g, function (match, i) {
                return attributeMap[i] || '';
            });
        }
    },

    mounted () {
        this.$isMounted = true;
        this.$forceUpdate();
    },

    render () {
        let me = this,
            text = me._renderHTML();
        return (
            <span class="language-wrap" title={me.showTitle ? text : ''}>
                {
                    text
                }
            </span>
        );
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章