xlsx.js 表格的導出與導入


1.xlsx簡介

通俗的說,xlsx這個插件可以把html中的table元素或者json數據轉換成表格后進行導出

<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.17.4/xlsx.js"></script>

2.基本概念

一個excel文件就是一個book,一個book中又包含一個或多個sheet,以下方法均由XLSX.utils對象調用

常用方法 參數 說明
book_new - 創建一個空的book
table_to_book table元素(dom) 將table元素轉換成 book
table_to_sheet table元素(dom) 將table元素轉換成 sheet
json_to_sheet json數據(數組) 將json數據轉換成 sheet
book_append_sheet 參數1:book對象,參數2:sheet對象,參數3:sheet名稱 將sheet添加到book中
writeFile 參數1:book對象,參數2:導出的文件名稱 導出表格文件

3.JSON轉表格

  • 基本代碼
 exportEvent () {
    // 要打印的數據格式 對象中的key將會作為表頭渲染
    var print_data = [
        {'序號':1,'姓名':'張三','年齡':20},
        {'序號':2,'姓名':'李四','年齡':25},
        {'序號':3,'姓名':'王五','年齡':30}
    ]
    // 創建一個新sheet
    const new_sheet = XLSX.utils.json_to_sheet(print_data)
    // 設置每列的列寬(可選),10代表10個字符,注意中文占2個字符
    new_sheet['!cols'] = [
        { wch: 10 },
        { wch: 30 },
        { wch: 25 }
    ]
    // 新建book
    const new_book = XLSX.utils.book_new()
    // 將 sheet 添加到 book 中
    XLSX.utils.book_append_sheet(new_book, new_sheet, '人員名單')
    // 導出excel文件
    XLSX.writeFile(new_book, '數據導出.xlsx')
}   
  • 表頭標題替換方法
replaceKeyForTableData(tableData = [],mapData = {}){
        //要返回的最終數據
        var result = []
        //循環數組的每行數據
        tableData.forEach(row=>{
            //新的行數據
            var new_row = {}
            //循環每個鍵值
            Object.keys(row).forEach(key=>{
                //判斷這個可以是否 可以在映色中找到對應的值 有則使用,無則使用原來的key
                var new_key = mapData[key]? mapData[key]:key
                //為新的行數據添加鍵值對
                new_row[new_key] = row[key]
            })
            //新的行添加到表格數據中
            result.push(new_row)
        })
        return result
    }

4.HTML-table轉表格

  • 導出原生表格:將table元素傳入即可導出
//獲取dom元素
var table_dom = document.querySelector('table')
//將dom轉換為book
const new_book = XLSX.utils.table_to_book(table_dom)
// 導出excel文件 如導出后的文件不能打開,請將后綴替換為 .xls
XLSX.writeFile(new_book, '數據導出.xlsx')
  • 導出插件生成的表格:插件生成的表格因為兼容一些功能,最終渲染的時候其實是2個table元素,為了能將表格完整的導出(thead+body),需要手動拼接dom
//這里以vxe-table為例
exportEvent () {
    // 創建新的table元素
    var print_table_dom = document.createElement('table')
    // copy一份thead
    var print_table_dom_thead = this.$refs.p_table.$el.querySelector('.vxe-table--header-wrapper > .vxe-table--header thead').cloneNode(true)
    // copy一份tbody
    var print_table_body = this.$refs.p_table.$el.querySelector('.vxe-table--body-wrapper > .vxe-table--body tbody').cloneNode(true)
    // 將thead和tbody添加到 目標table中
    print_table_dom.appendChild(print_table_dom_thead)
    print_table_dom.appendChild(print_table_body)
    // 生成 book
    const new_sheet = XLSX.utils.table_to_book(print_table_dom)
    // 導出excel
    XLSX.writeFile(new_sheet, '數據導出.xlsx')
},
  • 數字格式的問題:excel時,以0開頭的數據,其開頭0被忽略,如001234導出到excel后變為了1234,只需要將dom轉換為book的時候,傳入第二個參數即可
//將dom轉換為book,傳入第二個參數{raw:true}
const new_book = XLSX.utils.table_to_book(table_dom,{raw:true})

5.jsonToxlsx封裝

  • 1.表格轉換過程中,表頭可能需要替換
  • 2.表格列寬動態設定
  • 3.20230906升級:導出表格列的順序已表頭信息配置為准,自動過濾表頭沒有的列(表頭信息配置為空時,使用原邏輯)
  • 4.代碼已上傳至gitee
//此插件依賴xlsl.js
//@author zhoulianli 2022-04-15
//此插件主要用來導出表格數據,在xlsl.js基礎上封裝了列寬自適應,表頭標題替換功能

//使用方法 jsonToxlsx(參數)
//參數1: 表格數據 [{},{}]
//參數2:可選,表頭標題映射數據 {name:"姓名",age:"年齡"}
//參數3:可選,文件名 例如:"數據導出"
//參數4:可選,sheet名 例如:"能耗比數據"

//閉包  傳入當前環境的this
(function (global, factory) {
    //1.先判斷當前環境是否支持CommonJS規范(node.js)
    if (typeof exports == 'object' && typeof module !== 'undefined') {
        //console.log('CommonJS規范')
        module.exports = factory()
    } else if (typeof define == 'function' && define.amd) {//2.再判斷是否支持AMD規范(require.js)
        //console.log('AMD規范')
        define(factory)
    } else {
        //console.log('script標簽引入')
        //接收該對象
        this.jsonToxlsx = factory()
    }
}(this,function(){
    //將表格數組導出的方法
    function jsonToxlsx(tableData = [],tHeadMap = {},fileName = "數據導出",sheetName = "數據導出"){
        //判斷依賴是否存在
        if(typeof XLSX == undefined){
            console.log('請先引入XLSX.js')
            return
        }
        
        //判斷是否有表頭信息
        if(tHeadMap && Object.keys(tHeadMap).length > 0){
            //轉換數據格式2
            var print_data = transFormTableData2(tableData,tHeadMap)
        }else{
            //轉換數據格式1
            var print_data = transFormTableData(tableData,tHeadMap)
        }

        // 創建一個新sheet
        const new_sheet = XLSX.utils.json_to_sheet(print_data)

        // 根據每列數據智能設定列寬
        var two_dimensional_arr = transformToTwoDimensional(print_data)
        var width_arr = two_dimensional_arr.map(arr=>{
            return {
                wch: getMaxLenByArr(arr) + 2
            }
        })
        new_sheet['!cols'] = width_arr

        // 新建book
        const new_book = XLSX.utils.book_new()
        // 將 sheet 添加到 book 中
        XLSX.utils.book_append_sheet(new_book, new_sheet, sheetName)
        // 導出excel文件
        XLSX.writeFile(new_book, `${fileName}${Moment().format('YYYYMMDDHHmmss')}.xlsx`)
    }

    //轉換表格數據
    function transFormTableData(tableData = [],mapData = {}){
        //要返回的最終數據
        var result = []
        //循環數組的每行數據
        tableData.forEach(row=>{
            //新的行數據
            var new_row = {}
            //循環每個鍵值
            Object.keys(row).forEach(key=>{
                //判斷這個可以是否 可以在映色中找到對應的值 有則使用,無則使用原來的key
                var new_key = mapData[key]? mapData[key]:key
                //為新的行數據添加鍵值對
                new_row[new_key] = row[key]
            })
            //新的行添加到表格數據中
            result.push(new_row)
        })
        return result
    }

    //轉換表格數據2
    //表格行的屬性順序和數量由表頭配置來決定
    function transFormTableData2(tableData = [],mapData = {}){
        //要返回的最終數據
        var result = []
        //循環數組的每行數據
        tableData.forEach(row=>{
            //新的行數據
            var new_row = {}
            //循環表頭每個鍵值
            for(var key in mapData){
                var target_key = mapData[key]
                //給表格行添加字段
                new_row[target_key] = row[key]
            }
            //新的行添加到表格數據中
            result.push(new_row)
        })
        return result
    }

    //將表格數組轉換為二維數組 [[col1-1,col1-2],[col2-1,col2-2]]
    function transformToTwoDimensional(tableData){
        //如果數據源為空 則返回空
        if (tableData == 0) {
            return []
        }
        //對象有多少個key,就有多少列,每列數據是一個數組

        //先填充標題
        var result = []
        for (key in tableData[0]) {
            result.push([key])
        }

        //接着填充數據
        tableData.forEach(row => {
            var index = 0
            for (key in row) {
                result[index].push(row[key])
                index++
            }
        })

        return result
    }


    //從一維數組中提取出 最大的字節占位
    function getMaxLenByArr(strArr){
        var max = 0
        strArr.forEach(item => {
            //轉為字符串
            var str = item + ''
            var strlen = 0;
            for (var i = 0; i < str.length; i++) {
                if (str.charCodeAt(i) > 255) //如果是漢字,則字符串長度加2
                    strlen += 2;
                else
                    strlen++;
            }
            if( strlen > max){
                max = strlen
            }
        })
        return max
    }
    return jsonToxlsx;
}))

6.表格的導入

表格的導入一般經過幾個步驟:
1.文件讀取:借助文件域+H5的FileReader對文件進行讀取
2.一次轉碼:使用XLSX對FileReader讀取到的數據轉換成bookData
3.二次轉碼:再借助vxe-table中的方法將上個步驟的數據轉換成csvData
4.三次轉碼:將csvData轉換為json數據

值得說明的是,前面三個步驟調用的都是瀏覽器或者插件現成的接口,而且也不會涉及業務層面的邏輯,所以我們不去動它。而第四步將csvData轉換為json數據,目標json的數據格式需要根據業務來決定,所以第四步就值得探討一番

我先說結論,csvData轉換為json數據只能通過下標進行匹配,這也就意味着excel文件的列需要一個固定的格式,列的順序一旦出錯,導入就會有問題

這是excel表格數據:

這是轉換后的csvData:

以下代碼作為參考:

 data: {
            tableColumn:[
                {field:'id',title:'id'},
                {field:'name',title:'姓名'},
                {field:'role',title:'角色'},
                {field:'sex',title:'性別'},
                {field:'age',title:'年齡'},
                {field:'address',title:'地址'}
            ],
            tableData: [
                { id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' },
                { id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
                { id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
                { id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 24, address: 'Shanghai' }
            ]
},
methods:{
//選擇文件
            fileChange(){
                var file = document.querySelector('#file').files[0]
                //如果文件存在 則讀取
                if(file){
                    //創建reader對象
                    var reader = new FileReader()
                    //讀取選擇的文件
                    reader.readAsBinaryString(file)

                    //監聽讀取事件
                    var that = this
                    reader.onload = function(ev){
                        const data = ev.target.result
                        //轉換book對象
                        const workbook = XLSX.read(data, { type: 'binary' })
                        //將某個sheet轉換成csvData 注意 sheet 的名稱
                        const csvData = XLSX.utils.sheet_to_csv(workbook.Sheets['Sheet1'])
                        that.tableData = that.csvToJson(csvData,that.tableColumn)
                    }
                }
            },
            //將csv數據轉換為json數據
            csvToJson(csvData,tableColumn=[]){
                //1.一般情況下,表格數據與tableColumn根據下標來進行匹配
                //1.如果導入的表格有表頭,則與表頭與tableColumn進行匹配
                const tableData = []
                // 解析數據
                csvData.split('\n').forEach((vRow) => {
                    //遍歷行
                    if (vRow) {
                        const vCols = vRow.split(',')
                        const item = {}
                        //遍歷列
                        vCols.forEach((val, cIndex) => {
                            //根據列的下標找到對應的 field
                            const column = tableColumn[cIndex]
                            if (column.field) {
                                //添加字段
                                item[column.field] = val
                            }
                        })
                        tableData.push(item)
                    }
                })
                return tableData
            }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM