純前端JavaScript實(shí)現(xiàn)Excel IO案例分享
公司最近要為某國企做一個(gè)**統(tǒng)計(jì)和管理系統(tǒng),
具體要求包含
•Excel導(dǎo)入導(dǎo)出
•根據(jù)導(dǎo)入的數(shù)據(jù)進(jìn)行展示報(bào)表
•圖表展示(包括柱狀圖,折線圖,餅圖),而且還要求要有動(dòng)畫效果,扁平化風(fēng)格
•Excel導(dǎo)出,并要提供客戶端來管理Excel 文件
•...
要求真多!
現(xiàn)在總算是完成了,于是將我的經(jīng)驗(yàn)分析出來。
--------------------------------------------------------------------------------
在整個(gè)項(xiàng)目架構(gòu)中,首先就要解決Excel導(dǎo)入的問題。
由于公司沒有自己的框架做Excel IO,就只有通過其他渠道了。
嗯,我在github上找到了一個(gè)開源庫xlsx,通過npm方式來安裝。
npm install xlsx --save
之后,在自己的html文件里面添加對(duì)js文件的引用
<script src="./node_modules/xlsx/dist/jszip.js"></script>
<script src="./node_modules/xlsx/dist/xlsx.js"></script>
通過FileReader對(duì)象將數(shù)據(jù)以二進(jìn)制字符串的方式加載到內(nèi)存中,
target.addEventListener('drop', function (e) {
e.preventDefault();
handleDrop(e.dataTransfer.files[0]);
});
handleDrop = function(){
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
...
...
};
reader.readAsBinaryString(f);
}
然后我們下來的操作就是要利用庫對(duì)data數(shù)據(jù)進(jìn)行操作了。
它暴露了一個(gè)對(duì)象XLSX,通過XLSX的read() 方法就可以將數(shù)據(jù)讀為JSON對(duì)象了。
var workbook = XLSX.read(data, { type: 'binary' });
var sheetName = workbook.SheetNames[0];
var sheet = workbook.Sheets[sheetName];
之后,使用鍵值對(duì)的方式再把數(shù)據(jù)從sheet中取出來放到表格中。
var table = document.createElement('table');
for (var row = 1; ; row++) {
if (sheet['A' + row] == null) {
break;
}
var tr = document.createElement('tr');
for (var col = 65; col <= 90; col++) {
var c = String.fromCharCode(col);// get 'A', 'B', 'C' ...
var key = '' + c + row;
if (sheet[key] == null) {
break;
}
var td = document.createElement('td');
td.innerHTML = sheet[key]['w'];
tr.appendChild(td);
}
table.appendChild(tr);
}
document.querySelector('#target').appendChild(table);
下面是完整代碼:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#target {
height: 400px;
width: 700px;
background-color: #f8f8f8;
margin: 200px auto;
overflow:hidden;
border-radius:5px;
box-shadow:2px 2px 5px #888;
}
.hover::before {
content: '請(qǐng)將excel文件拖到這里';
width: 100%;
height: 100%;
display: block;
text-align: center;
line-height: 400px;
font-size: 24px;
font-family: '微軟雅黑';
}
#target>table{
height:250px;
width:400px;
border:1px solid #ccc;
border-radius:3px;
margin:75px auto;
}
#target>table td{
text-align:center;
border-top:1px solid #ccc;
border-left:1px solid #ccc;
}
#target>table tr:first-child>td{
border-top:0px solid #ccc;
}
#target>table tr>td:first-child{
border-left:0px solid #ccc;
}
</style>
</head>
<body>
<div id="target" class="hover">
</div>
<script src="./node_modules/xlsx/dist/jszip.js"></script>
<script src="./node_modules/xlsx/dist/xlsx.js"></script>
<script src="index.js"></script>
</body>
</html>
下面是完整js代碼
index.js
window.addEventListener('load', function () {
var target = document.querySelector('#target');
target.addEventListener('dragenter', function () {
this.classList.remove('hover');
});
target.addEventListener('dragleave', function () {
this.classList.add('hover');
});
target.addEventListener('dragover', function (e) {
this.classList.remove('hover');
e.preventDefault();
});
target.addEventListener('drop', function (e) {
e.preventDefault();
handleDrop(e.dataTransfer.files[0]);
});
});
var handleDrop = function (f) {
var reader = new FileReader(),
name = f.name;
reader.onload = function (e) {
var data = e.target.result,
workbook = XLSX.read(data, { type: 'binary' }),
sheetName = workbook.SheetNames[0],
sheet = workbook.Sheets[sheetName],
table = document.createElement('table');
for (var row = 1; ; row++) {
if (sheet['A' + row] == null) {
break;
}
var tr = document.createElement('tr');
for (var col = 65; col <= 90; col++) {
var c = String.fromCharCode(col);// get 'A', 'B', 'C' ...
var key = '' + c + row;
if (sheet[key] == null) {
break;
}
var td = document.createElement('td');
td.innerHTML = sheet[key]['w'];
tr.appendChild(td);
}
table.appendChild(tr);
}
document.querySelector('#target').appendChild(table);
};
reader.readAsBinaryString(f);
}
效果如下:

這樣做好像可行,但是我們很快就放棄了。
弊端太多了。
•這個(gè)庫現(xiàn)在目前還處于開發(fā)階段,在issues里面還有很多的Bug被提出。這沒有辦法保證最終網(wǎng)站的穩(wěn)定性。
•這個(gè)庫沒有辦法導(dǎo)入合并單元格的數(shù)據(jù),只能是很死板的按照'A', 'B', 'C'... 和1, 2, 3 坐標(biāo)來查詢數(shù)據(jù),而且它要求內(nèi)部單元格不能為空。
•更比較不方便的就是,它沒有行和列的計(jì)數(shù)的屬性。
•由于這是為國企做的,所以無法將關(guān)鍵功能依賴于這個(gè)star量不是很多的庫,降低風(fēng)險(xiǎn),也是為了網(wǎng)站的安全性。
•...
--------------------------------------------------------------------------------
經(jīng)過小組探討,我們決定使用另外一款前端控件,叫做 Wijmo。
首先,從網(wǎng)站上下載Wijmo包,這個(gè)控件沒有提供npm和bower等方式。
然后將我需要的包導(dǎo)入進(jìn)來
<script src="./wijmo/dist/controls/wijmo.min.js"></script> <script src="./wijmo/dist/controls/wijmo.grid.min.js"></script> <script src="./wijmo/dist/controls/wijmo.grid.detail.min.js"></script> <script src="./wijmo/dist/controls/wijmo.grid.xlsx.min.js"></script> <script src="./wijmo/dist/controls/wijmo.xlsx.min.js"></script>
此外,還有引入一個(gè)jszip的包,是使用js來解壓壓縮包的一個(gè)庫。(由于MS的open xml技術(shù),xlsx文件都可以解壓成為xml文件,app.xml 里包含了主要的數(shù)據(jù))。
<script src="./jszip.min.js"></script>
讀取文件的操作和上面都是一樣的
var handleDrop = function (file) {
var reader,
workbook;
if (file) {
reader = new FileReader;
reader.onload = function (e) {
workbook = new wijmo.xlsx.Workbook(),
workbook.load(reader.result);
};
reader.readAsDataURL(file);
}
}
通過
workbook = new wijmo.xlsx.Workbook();
workbook.load(reader.result);
這兩行代碼將excel文件加載到內(nèi)存 中的workbook對(duì)象。
打印workbook對(duì)象

打印這個(gè)對(duì)象發(fā)現(xiàn),workbook里面包含sheets數(shù)組,每個(gè)sheet包含rows數(shù)組,每個(gè)row包含cells數(shù)組,每個(gè)cell里面vaule屬性就是單元格的值。
這簡直太又好了
下面實(shí)現(xiàn)一個(gè)函數(shù) getCollectionView ,以對(duì)象數(shù)組的方式來獲取數(shù)據(jù)
var getCollectionView = function (workbook) {
var collectionView = [];
if (workbook) {
var sheet = workbook.sheets[0],
header = []; // 列標(biāo)題數(shù)組
for (var i = 0, length = sheet.rows.length; i < length; i++) {
var row = sheet.rows[i],
rowArray = {};
for (var j = 0, jLength = row.cells.length; j < jLength; j++) {
var cell = row.cells[j];
// 如果是第一行數(shù)據(jù),那么是作為列標(biāo)題出現(xiàn)的,就放進(jìn)標(biāo)題數(shù)組中
if (i === 0) {
header.push(cell.value);
}
else {
// 后面的行數(shù)組,就作為rowArray對(duì)象的屬性存儲(chǔ),屬性名就是該列的標(biāo)題。
rowArray[header[j]] = cell.value;
}
}
if (i !== 0) {
collectionView.push(rowArray);
}
}
}
return collectionView;
}
然后需要一個(gè)表格將數(shù)據(jù)呈現(xiàn)出來,這里我直接使用了Wijmo的FlexGrid表格。
gridDiv = document.createElement('div');
gridDiv.classList.add('grid');
dataGrid = new wijmo.grid.FlexGrid(gridDiv);// 通過傳入容器構(gòu)造一個(gè)FlexGrid表單。
var collectionView = new wijmo.collections.CollectionView(getCollectionView(workbook));
dataGrid.itemsSource = collectionView;
好了,經(jīng)過上面幾個(gè)步驟,導(dǎo)入Excel到表格已經(jīng)實(shí)現(xiàn)了
這是完整的js代碼:
index.js
(function () {
var dataGrid = null,
gridDiv = null,
workbook = null;
window.addEventListener('load', function () {
gridDiv = document.createElement('div');
gridDiv.classList.add('grid');
dataGrid = new wijmo.grid.FlexGrid(gridDiv);
var target = document.querySelector('#target');
target.addEventListener('dragenter', function (e) {
e.preventDefault();
this.classList.remove('hover');
});
target.addEventListener('dragleave', function (e) {
e.preventDefault();
this.classList.add('hover');
});
target.addEventListener('dragover', function (e) {
e.preventDefault();
this.classList.remove('hover');
});
target.addEventListener('drop', function (e) {
e.preventDefault();
handleDrop(e.dataTransfer.files[0]);
// 將這個(gè)表單添加到頁面上
this.appendChild(gridDiv);
});
});
var handleDrop = function (file) {
var reader;
var workbook;
if (file) {
reader = new FileReader;
reader.onload = function (e) {
workbook = new wijmo.xlsx.Workbook();
workbook.load(reader.result);
var collectionView = new wijmo.collections.CollectionView(getCollectionView(workbook));
dataGrid.itemsSource = collectionView;
// console.log(dataGrid.collectionView);
};
reader.readAsDataURL(file);
}
}
var getCollectionView = function (workbook) {
var collectionView = [];
if (workbook) {
var sheet = workbook.sheets[0];
var title = [];
for (var i = 0, length = sheet.rows.length; i < length; i++) {
var row = sheet.rows[i];
var rowArray = {};
for (var j = 0, jLength = row.cells.length; j < jLength; j++) {
var cell = row.cells[j];
if (i === 0) {
header.push(cell.value);
}
else {
rowArray[header[j]] = cell.value;
}
}
if (i !== 0) {
collectionView.push(rowArray);
}
}
}
return collectionView;
}
})(window);
下面是效果

Excel 導(dǎo)出
歐了
兩句代碼實(shí)現(xiàn)Excel導(dǎo)出功能
wijmo.grid.xlsx.FlexGridXlsxConverter.save(dataGrid,
{ includeColumnHeaders: true }, fileName);
這個(gè)表格還支持過濾,分組,篩選,編輯。
面積圖和柱狀圖
就在完成Excel IO 之后,發(fā)現(xiàn)這個(gè)控件包還可以做面積圖,柱狀圖和其他很多類型的圖形。
所以在這里就演示一個(gè)面積圖的和一個(gè)柱狀圖的例子。
首先,要將包引進(jìn)來。
<script src="./wijmo/dist/controls/wijmo.chart.min.js"></script>
然后經(jīng)過下面幾句代碼,就可以使用在頁面中插入一個(gè)柱狀圖
chart = new wijmo.chart.FlexChart('#chart');
chart.initialize({
itemsSource: collectionView,
bindingX: 'name',
options: {
groupWidth: 15
},
series: [
{ name: '年齡', binding: 'age' },
]
});
下面看效果

其中,顏色和柱狀圖的形狀可以調(diào)整的。當(dāng)鼠標(biāo)移到元素上,還有會(huì)小提示。
在這里,只需要改變一下chart的類型,就可以切換為其他類型的圖表
chart.chartType = chart.chartType === wijmo.chart.ChartType.Column ? wijmo.chart.ChartType.Area : wijmo.chart.ChartType.Column;
關(guān)于本篇的代碼已經(jīng)上傳 http://xiazai.jb51.net/201608/yuanma/js-xlsWijmo-IO(jb51.net).rar
后續(xù)會(huì)托管到github.
最終還是比較快的完成了任務(wù)。
關(guān)于這個(gè)項(xiàng)目的Excel IO 就簡單介紹到這里,這個(gè)項(xiàng)目現(xiàn)在已經(jīng)完成了,后續(xù)會(huì)分享一些其他的技術(shù)細(xì)節(jié)。
希望可以對(duì)你提供幫助。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- js導(dǎo)入導(dǎo)出excel(實(shí)例代碼)
- JS Excel讀取和寫入操作(模板操作)實(shí)現(xiàn)代碼
- 可以讀取EXCEL文件的js代碼
- JavaScript將頁面表格導(dǎo)出為Excel的具體實(shí)現(xiàn)
- 通過Javascript讀取本地Excel文件內(nèi)容的代碼示例
- Js 導(dǎo)出table內(nèi)容到Excel的簡單實(shí)例
- javascript Excel操作知識(shí)點(diǎn)
- JS兼容瀏覽器的導(dǎo)出Excel(CSV)文件的方法
- JS將表單導(dǎo)出成EXCEL的實(shí)例代碼
- JS 巧妙獲取剪貼板數(shù)據(jù) Excel數(shù)據(jù)的粘貼
相關(guān)文章
JS字符串累加Array不一定比字符串累加快(根據(jù)電腦配置)
言歸正傳:性能差異較大的機(jī)器運(yùn)行結(jié)果會(huì)RT 出現(xiàn)大的差異,為了保險(xiǎn)起見。還是推薦使用Array 來進(jìn)行字符串拼接操作2012-05-05
跟我學(xué)Node.js(四)---Node.js的模塊載入方式與機(jī)制
Node.js中模塊可以通過文件路徑或名字獲取模塊的引用。模塊的引用會(huì)映射到一個(gè)js文件路徑,除非它是一個(gè)Node內(nèi)置模塊。Node的內(nèi)置模塊公開了一些常用的API給開發(fā)者,并且它們?cè)贜ode進(jìn)程開始的時(shí)候就預(yù)加載了。2014-06-06
javascript 數(shù)組排序與對(duì)象排序的實(shí)例
這篇文章主要介紹了javascript 數(shù)組排序與對(duì)象排序的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-07-07
深入理解javascript構(gòu)造函數(shù)和原型對(duì)象
對(duì)象,是javascript中非常重要的一個(gè)梗,是否能透徹的理解它直接關(guān)系到你對(duì)整個(gè)javascript體系的基礎(chǔ)理解,說白了,javascript就是一群對(duì)象在攪。。(嗶?。?。2014-09-09
JavaScript?canvas?實(shí)現(xiàn)用代碼畫畫
這篇文章主要為大家介紹了JavaScript?canvas?實(shí)現(xiàn)用代碼畫畫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
用js實(shí)現(xiàn)圖片旋轉(zhuǎn)的兩種方案
這篇文章主要給大家介紹了關(guān)于用js實(shí)現(xiàn)圖片旋轉(zhuǎn)的兩種方案, 旋轉(zhuǎn)的效果就是根據(jù)鼠標(biāo)的的移動(dòng)距離來顯示不同的圖片,形成視覺差,仿佛就是在正真的旋轉(zhuǎn),需要的朋友可以參考下2023-07-07
spirngmvc js傳遞復(fù)雜json參數(shù)到controller的實(shí)例
下面小編就為大家分享一篇spirngmvc js傳遞復(fù)雜json參數(shù)到controller的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-03-03
JS獲取當(dāng)前時(shí)間的年月日時(shí)分秒及時(shí)間的格式化的方法
這篇文章主要介紹了js獲取當(dāng)前時(shí)間的年月日時(shí)分秒及時(shí)間的格式化,本文通過實(shí)例代碼講解的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
javascript offsetX與layerX區(qū)別
FF沒有offsetX屬性,有個(gè)layerX屬性,只要將事件源的位置設(shè)置成相對(duì)定位(position:relative)或絕對(duì)定位(position:absolute),兩者結(jié)果就相等,表示事件源相對(duì)于父元素的X坐標(biāo)。2010-03-03

