网络日志

前端表格处理知多少?

前言

之前做表格导出功能都是调用后端接口,由后端生成表格链接进行下载的,其实前端也是可以自己生成表格进行下载。

业务场景

在众多企业后台管理中经常会遇到会需要导出页面统计类的表格或者输出打印类的图表,作为前端处理此类需求,我能想到的也就2中解决方向,一个是利用现有的表格类插件,二是利用canvas绘制图片导出。目前好多企业都有大数据的需求,那不免对一些表格的数据需要进行处理。可以利用xlsx/csv进行处理。

csv表格处理

表格处理最简单的就是csv,对于页面中的table数据做转化处理就可以导出,有一定的局限性,个性化能力较弱,个性化强的需求可以用XLSX插件处理。

导出csv表格处理

导出csv表格处理最主要的是处理单元格内的换行及字符编码问题处理。

let str = ``
let columns = [表格头部列数组]
let dataSource = [表格数据数组]
columns.forEach((element) => {
    str += `${表格头部列名},`
})
/**
* , 表格换行
* \r\n 单元格换行,外层包裹双引号""
*/
dataSource.map((item) => {
    str +=`\r\n${行内容},"${单元格内容}\r\n${单元格内容}\r\n${单元格内容}"`
})
//encodeURIComponent解决中文乱码
let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str)
let link = document.createElement('a')
link.href = uri
link.download = `${表格名称}.csv`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)

canvas图片处理

另外一种处理方式就是canvas处理,目前处理canvas最常用的插件就是html2canvas了,在前端常常是封装成导图组件使用。

const imageWrapper = 需要绘制的HTML标签
const clientWidth = imageWrapper.offsetWidth
const clientHeight = imageWrapper.offsetHeight
const kh = [clientWidth, clientHeight]
html2canvas(imageWrapper, {
    useCORS: true,
    logging: true
}).then((canvas) => {
    const dataURL = canvas.toDataURL('image/png')
    const getUrlBase64 = (url, kh) => {
        return new Promise((resolve) => {
            let canvas = document.createElement('canvas')
            const ctx = canvas.getContext('2d')
            const img = new Image()
            img.crossOrigin = 'Anonymous' // 允许跨域
            img.src = url
            img.onload = () => {
                canvas.height = kh[1]
                canvas.width = kh[0]
                ctx.drawImage(img, 0, 0, kh[0], kh[1])
                const dataURL = canvas.toDataURL('image/png')
                canvas = null
                resolve(dataURL)
            }
        })
    }
    getUrlBase64(dataURL, kh).then((base64) => {
        // 下载
        const link = document.createElement('a')
        link.href = base64
        link.download = `Loan Statement.png`
        link.click()
        // 新页面打开
        let arr = base64.split(',') //去掉base64格式图片的头部
        let bstr = atob(arr[1]) //atob()方法将数据解码
        let leng = bstr.length
        let u8arr = new Uint8Array(leng)
        while (leng--) {
            u8arr[leng] = bstr.charCodeAt(leng) //返回指定位置的字符的 Unicode 编码
        }
        window.open(window.URL.createObjectURL(new File([u8arr], `${图片名称}`, { type: 'image/png' })))
    })
})

针对大数据表格处理类的需求

这类需求主要是针对大量表格需要对符合正则的数据批量替换成大数据统计需要的一些标识。就需要识别当前的表格内容,然后对数据进行解析再替换,最后导出,以下给出的是需要人为处理的的单个方案,感兴趣的可以集成为自动批量处理。利用的是xlsx插件处理导入的数据解析,及导出

const isUTF8(bytes)=> {
    var i = 0
    while (i < bytes.length) {
        if (
            // ASCII
            bytes[i] == 0x09 ||
            bytes[i] == 0x0a ||
            bytes[i] == 0x0d ||
            (0x20 <= bytes[i] && bytes[i] <= 0x7e)
        ) {
            i += 1
            continue
        }

        if (
            // non-overlong 2-byte
            0xc2 <= bytes[i] &&
            bytes[i] <= 0xdf &&
            0x80 <= bytes[i + 1] &&
            bytes[i + 1] <= 0xbf
        ) {
            i += 2
            continue
        }

        if (
            // excluding overlongs
            (bytes[i] == 0xe0 && 0xa0 <= bytes[i + 1] && bytes[i + 1] <= 0xbf && 0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xbf) || 
            // straight 3-byte
            (((0xe1 <= bytes[i] && bytes[i] <= 0xec) || bytes[i] == 0xee || bytes[i] == 0xef) && 0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xbf && 0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xbf) || 
            // excluding surrogates
            (bytes[i] == 0xed && 0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x9f && 0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xbf)
        ) {
            i += 3
            continue
        }

        if (
            // planes 1-3
            (bytes[i] == 0xf0 &&
                    0x90 <= bytes[i + 1] &&
                    bytes[i + 1] <= 0xbf &&
                    0x80 <= bytes[i + 2] &&
                    bytes[i + 2] <= 0xbf &&
                    0x80 <= bytes[i + 3] &&
                    bytes[i + 3] <= 0xbf) || // planes 4-15
            (0xf1 <= bytes[i] &&
                    bytes[i] <= 0xf3 &&
                    0x80 <= bytes[i + 1] &&
                    bytes[i + 1] <= 0xbf &&
                    0x80 <= bytes[i + 2] &&
                    bytes[i + 2] <= 0xbf &&
                    0x80 <= bytes[i + 3] &&
                    bytes[i + 3] <= 0xbf) || // plane 16
            (bytes[i] == 0xf4 &&
                    0x80 <= bytes[i + 1] &&
                    bytes[i + 1] <= 0x8f &&
                    0x80 <= bytes[i + 2] &&
                    bytes[i + 2] <= 0xbf &&
                    0x80 <= bytes[i + 3] &&
                    bytes[i + 3] <= 0xbf)
        ) {
            i += 4
            continue
        }
        return false
    }
    return true
}
readFileType(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = function (evt) {
            resolve(isUTF8(new Uint8Array(evt.target.result)))
        }
        reader.readAsArrayBuffer(file)
    })
}
let reader = new FileReader()
let isCSV = file.name.split('.').pop().toLowerCase() === 'csv'
if (isCSV) {
    reader.readAsArrayBuffer(file)
    const isUTF8 = await _this.readFileType(file)
    isUTF8 ? reader.readAsText(file) : reader.readAsText(file, 'gbk')
} else {
    reader.readAsBinaryString(file)
}
reader.onload = function (e) {
    let data = e.target.result
    let workbook = XLSX.read(data, { type: 'binary' })
    let sheetNames = workbook.SheetNames // 工作表名称集合
    let resultData = []
    sheetNames.forEach((name) => {
        let worksheet = workbook.Sheets[name] // 只能通过工作表名称来获取指定工作表
        resultData = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
    })
    // 再对resultData数据json进行转换成表格所需数据进行展示
}
// 导出xlxs表格
const data = 表格数据
let option = {} //option代表的就是excel文件
let dataTable = [] //excel文件中的数据内容
data.forEach((element) => {
    element.author = this.XLSXAuthor
    delete element.regExpMatch
})
let len = data.length
if (len) {
    for (let i = 0; i < len; i++) {
        let row = data[i]
        let obj = {}
        for (let key in row) {
            if (typeof row[key] === 'object') {
                let arr = row[key]
                obj[key] = arr.map((item) => (typeof item === 'object' ? item.label : item)).join(',')
            } else {
                obj[key] = row[key]
            }
        }
        dataTable.push(obj) //设置excel中每列所获取的数据源
    }
}
let tableKeys = Object.keys(dataTable[0])
option.fileName = localStorage.getItem('XLSXFileName') //excel文件名称
option.datas = [
    {
        sheetData: dataTable, //excel文件中的数据源
        sheetName: localStorage.getItem('XLSXFileName'), //excel文件中sheet页名称
        sheetFilter: tableKeys, //excel文件中需显示的列数据
        sheetHeader: tableKeys //excel文件中每列的表头名称
    }
]
let toExcel = new ExportJsonExcel(option) //生成excel文件
toExcel.saveExcel() //下载excel文件

这个案例是导入xlsx/csv表格,拿到表格数据再进行展示在页面,人为修改后再进行导出xlsx,如果想再深入一点,可以使用node编写一个转换表格数据的服务。这个就需要配合一些大数据的一些规则提取去实现。

结语

本次分享的都是在工作中遇到的表格的一些解决方法,个人的一些浅见,写的不好请轻喷!!!!欢迎一起交流!!!!

前端表格处理知多少?