sheetjs-js读取excel

参考文档:

先看这个:如何使用JavaScript实现纯前端读取和导出excel文件-好记的博客 GitHub - rockboom/SheetJS-docs-zh-CN: SheetJS中文文档,版本v0.14.0,持续更新中 GitHub - SheetJS/sheetjs: SheetJS Community Edition -- Spreadsheet Data Toolkit 使用 js-xlsx 处理 Excel 文件 - 星陨的菲 - 博客园 文件拖拽参考:JS实现的文件拖拽上传功能示例_javascript技巧_脚本之家

示例代码

<!DOCTYPE html>
<html lang="zh-cn">

<head>
    <meta charset="UTF-8">
    <title>权限点制品生成</title>
    <style type="text/css">
        #box {
            width: 50%;
            height: 50%;
            border: 1px dashed #000;
            position: fixed;
            top: 25%;
            left: 25%;
            text-align: center;
            font: 20px/300px '微软雅黑';
            display: none;
            z-index: 999;
            background-color: rgb(128 128 128 / 50%);
        }

        table {
            border-collapse: collapse;
        }

        label {
            padding-left: 20px;
        }

        th,
        td {
            border: solid 1px #6D6D6D;
            padding: 5px 10px;
        }

        .mt-sm {
            margin-top: 8px;
        }

        body {
            background: #f4f4f4;
            padding: 0;
            margin: 0;
        }

        .container {
            width: 1024px;
            margin: 0 auto;
            background: #fff;
            padding: 20px;
            min-height: 100vh;
        }

        #verInfo {
            position: absolute;
            bottom: 2px;
        }
    </style>
</head>

<body>
    <div id="box">请将文件拖拽到此区域</div>
    <div class="container">
        <h1>步骤一:下载权限点原始文件</h1>
        <a href="https://kdocs.cn/fl/siUVrI5eP" target="_blank">权限点文档下载</a>
        <h1>步骤二:加载权限点Excel</h1>
        <div class="mt-sm">
            <input type="file" id="file" style="display:none;"
                accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
            <a href="javascript:selectFile()">加载本地excel文件</a> 或 <span style="color:red"><b>拖拽文件至该窗口</b></span>
            <div id="filePath"></div>
        </div>

        <h1>步骤三:选择套餐和项目</h1>
        <p>套餐</p>
        <div id="taocan"></div>
        <p>项目</p>
        <div id="project"></div>
        <p>附加选项</p>
        <div>
            <input type="checkbox" id="restOp" checked disabled>采用全量导入模式(禁止修改)
            <p style="color:red">全量导入模式下,导入权限点制品时,“是否清空原有权限点?”必须选择是</p>
        </div>

        <h1>步骤四:导出权限点制品</h1>
        <div class="mt-sm" style="padding-bottom:40px;">
            <input id="exportFile" type="button" onclick="exportExcel()" value="导出权限点制品" />
        </div>
        <div class="mt-sm" style="padding-bottom:40px;" id="error">

        </div>
        <div id="verInfo">
            <p style="font-size: small;color: grey;">该工具可以将产品维护的权限点文档处理成导入使用的制品文件,处理内容包括:公式转文本、无用列清理、非套餐权限点过滤、非项目权限点过滤。
            </p>
            <p>@Rizon ver:2022-03-23</p>

        </div>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.18.4/xlsx.core.min.js"></script>

    <script type="text/javascript">

        function selectFile() {
            document.getElementById('file').click();
        }

        // 读取本地excel文件
        function readWorkbookFromLocalFile(file, callback) {
            var reader = new FileReader();
            reader.onload = function (e) {
                var data = e.target.result;
                var workbook = XLSX.read(data, { type: 'binary' });
                if (callback) callback(workbook);
            };
            reader.readAsBinaryString(file);
        }


        // 读取 excel文件
        function outputWorkbook(workbook) {
            var sheetNames = workbook.SheetNames; // 工作表名称集合
            sheetNames.forEach(name => {
                var worksheet = workbook.Sheets[name]; // 只能通过工作表名称来获取指定工作表
                for (var key in worksheet) {
                    // v是读取单元格的原始值
                    console.log(key, key[0] === '!' ? worksheet[key] : worksheet[key].v);
                }
            });
        }
        let _worksheets;
        let taocan = [];
        let projects = [];
        let selectedTaocan = null;
        let selectedProject = null;
        function readWorkbook(workbook) {

            // var sheetNames = workbook.SheetNames; // 工作表名称集合
            var worksheet = workbook.Sheets["平台权限"]; // 这里我们只读取第一张sheet
            console.log(worksheet)
            _worksheets = workbook.Sheets;
            //删除筛选
            delete worksheet["!autofilter"];
            //获取套餐
            readTaocan(worksheet);
            renderTaocan();
            //获取项目
            readProjects(worksheet);
            renderProjects();
            //清理公式
            formual2txt(worksheet);


            // var csv = XLSX.utils.sheet_to_csv(worksheet);
            // document.getElementById('result').innerHTML = csv2table(csv);
        }
        function renderTaocan() {
            selectedTaocan = null;
            let content = "";
            for (let t in taocan) {
                content += '<label>' + taocan[t] + '</label><input name="taocanRadio" type="radio" id="' + taocan[t] + '">'
            }
            $('#taocan').html(content);
            $('input:radio[name="taocanRadio"]').change(function (e) {
                console.log(e.target.id);
                selectedTaocan = e.target.id;
            })
        }
        function renderProjects() {
            selectedProject = null;
            let content = "";
            for (let t in projects) {
                content += '<label>' + projects[t] + '</label><input name="projectRadio" type="radio" id="' + projects[t] + '">'
            }
            $('#project').html(content);
            $('input:radio[name="projectRadio"]').change(function (e) {
                console.log(e.target.id);
                selectedProject = e.target.id;
            })
            $('input:radio[id="标品"]').attr("checked", true);
            selectedProject = "标品";
        }
        function readTaocan(ws) {
            taocan = [];
            const range = XLSX.utils.decode_range(ws['!ref']);
            for (let i = 3; i <= range.e.r + 1; i++) {//第三行开始
                if (ws["F" + i] && ws["F" + i]["w"]) {
                    if (taocan.indexOf(ws["F" + i]["w"]) < 0) {
                        taocan.push(ws["F" + i]["w"]);
                    }
                }
            }
        }
        function readProjects(ws) {
            projects = [];
            const range = XLSX.utils.decode_range(ws['!ref']);
            for (let i = 3; i <= range.e.r + 1; i++) {//第三行开始
                if (ws["G" + i] && ws["G" + i]["w"]) {
                    ws["G" + i]["w"].split(";").forEach(e => {
                        if (projects.indexOf(e) < 0) {
                            projects.push(e);
                        }
                    });
                }
            }
        }

        function formual2txt(ws) {
            const range = XLSX.utils.decode_range(ws['!ref']);
            for (let col = range.s.c; col <= range.e.c; col++) {
                for (let row = range.s.r; row <= range.e.r; row++) {
                    let address = encodeCell(row, col);
                    //删除公式
                    if (ws[address] && ws[address]['f']) {
                        delete ws[address]['f'];
                    }
                }
            }
        }
        function delCols() {
            var worksheet = _worksheets["平台权限"];
            //删除0-7列
            for (i = 0; i < 7; i++) {
                deleteCol(worksheet, 0);
            }
            //迭代-环境 6个
            for (i = 0; i < 6; i++) {
                deleteCol(worksheet, 5);
            }
            //角色 12个
            for (i = 0; i < 12; i++) {
                deleteCol(worksheet, 18);
            }
        }
        function encodeCell(r, c) {
            return XLSX.utils.encode_cell({ r, c });
        }
        function deleteRow(ws, index) {
            const range = XLSX.utils.decode_range(ws['!ref']);

            for (let row = index; row < range.e.r; row++) {
                for (let col = range.s.c; col <= range.e.c; col++) {
                    ws[encodeCell(row, col)] = ws[encodeCell(row + 1, col)];
                }
            }

            range.e.r--;

            ws['!ref'] = XLSX.utils.encode_range(range.s, range.e);
        }
        function deleteCol(ws, index) {
            const range = XLSX.utils.decode_range(ws['!ref']);

            for (let col = index; col < range.e.c; col++) {
                for (let row = range.s.r; row <= range.e.r; row++) {
                    ws[encodeCell(row, col)] = ws[encodeCell(row, col + 1)];
                }
            }

            range.e.c--;

            ws['!ref'] = XLSX.utils.encode_range(range.s, range.e);
        }
        // 将csv转换成表格
        function csv2table(csv) {
            var html = '<table>';
            var rows = csv.split('\n');
            rows.pop(); // 最后一行没用的
            rows.forEach(function (row, idx) {
                var columns = row.split(',');
                columns.unshift(idx + 1); // 添加行索引
                if (idx == 0) { // 添加列索引
                    html += '<tr>';
                    for (var i = 0; i < columns.length; i++) {
                        html += '<th>' + (i == 0 ? '' : String.fromCharCode(65 + i - 1)) + '</th>';
                    }
                    html += '</tr>';
                }
                html += '<tr>';
                columns.forEach(function (column) {
                    html += '<td>' + column + '</td>';
                });
                html += '</tr>';
            });
            html += '</table>';
            return html;
        }

        function table2csv(table) {
            var csv = [];
            $(table).find('tr').each(function () {
                var temp = [];
                $(this).find('td').each(function () {
                    temp.push($(this).html());
                })
                temp.shift(); // 移除第一个
                csv.push(temp.join(','));
            });
            csv.shift();
            return csv.join('\n');
        }

        // csv转sheet对象
        function csv2sheet(csv) {
            var sheet = {}; // 将要生成的sheet
            csv = csv.split('\n');
            csv.forEach(function (row, i) {
                row = row.split(',');
                if (i == 0) sheet['!ref'] = 'A1:' + String.fromCharCode(65 + row.length - 1) + (csv.length - 1);
                row.forEach(function (col, j) {
                    sheet[String.fromCharCode(65 + j) + (i + 1)] = { v: col };
                });
            });
            return sheet;
        }

        // 导出配置
        function sheet2blob() {

            var workbook = {
                SheetNames: ['平台权限', '平台角色'],
                Sheets: { '平台权限': _worksheets['平台权限'], '平台角色': _worksheets['平台角色'] }
            };

            // 生成excel的配置项
            var wopts = {
                bookType: 'xlsx', // 要生成的文件类型
                bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
                type: 'binary'
            };
            var wbout = XLSX.write(workbook, wopts);
            var blob = new Blob([s2ab(wbout)], { type: "application/octet-stream" });
            // 字符串转ArrayBuffer
            function s2ab(s) {
                var buf = new ArrayBuffer(s.length);
                var view = new Uint8Array(buf);
                for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
                return buf;
            }
            return blob;
        }

        /**
         * 通用的打开下载对话框方法,没有测试过具体兼容性
         * @param url 下载地址,也可以是一个blob对象,必选
         * @param saveName 保存文件名,可选
         */
        function openDownloadDialog(url, saveName) {
            if (typeof url == 'object' && url instanceof Blob) {
                url = URL.createObjectURL(url); // 创建blob地址
            }
            var aLink = document.createElement('a');
            aLink.href = url;
            aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
            var event;
            if (window.MouseEvent) event = new MouseEvent('click');
            else {
                event = document.createEvent('MouseEvents');
                event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            }
            aLink.dispatchEvent(event);
        }
        let restOp;
        function delNotIncludeRows() {
            restOp = $('#restOp').prop('checked');
            console.log('restOp', restOp);
            var ws = _worksheets["平台权限"];
            if (!selectedTaocan && !confirm("没有选择套餐,将会导出所有权限点,是否继续?")) {
                return "cancel";
            }
            //第三行开始
            for (; ;) {
                let r = delRowNext(ws, 3);
                if (r > 0) {
                    delRowNext(ws, r);
                } else {
                    break;
                }
            }
        }
        function delRowNext(ws, startRow) {
            const range = XLSX.utils.decode_range(ws['!ref']);
            for (let i = startRow; i <= range.e.r + 1; i++) {//第二行开始
                //套餐清理
                let address = "F" + i;
                if (selectedTaocan === "低") {
                    if (ws[address] && (ws[address]["w"] === "中" || ws[address]["w"] === "高")) {
                        deleteRow(ws, i - 1);
                        return i;
                    }
                } else if (selectedTaocan === "中") {
                    if (ws[address] && (ws[address]["w"] === "高")) {
                        deleteRow(ws, i - 1);
                        return i;
                    }
                }

                //项目清理
                address = "G" + i;
                if (ws[address] && ws[address]["w"]) {
                    let val = ws[address]["w"];
                    if (val.indexOf("标品") < 0 && val.indexOf(selectedProject) < 0) {
                        console.log("del", val, i - 1);
                        deleteRow(ws, i - 1);
                        return i;
                    }
                }

                //标记为删除的权限点 会清理掉
                if (restOp) {
                    address = "AE" + i;
                    if (ws[address] && ws[address]["v"]) {
                        let val = ws[address]["v"];
                        if (val === "") {
                            throw new Error("操作列单元格有空值,取消导出");
                        }
                        if (val.indexOf("修改") > -1) {
                            //改为新增
                            ws[address]["v"] = '新增';
                            delete ws[address]["w"];
                        } else if (val.indexOf("删除") > -1) {
                            //删除
                            console.log("del", val, i - 1);
                            deleteRow(ws, i - 1);
                            return i;
                        }
                    } else {
                        throw new Error("操作列单元格有空值,取消导出");
                    }
                }

            }
            return -1;
        }
        function readFile(file) {
            $('#filePath').html(file.name);
            readWorkbookFromLocalFile(file, function (workbook) {
                readWorkbook(workbook);
                document.getElementById("exportFile").value = "导出权限点制品";
                document.getElementById("exportFile").disabled = false;
            });
        }

        let _file;
        $(function () {
            document.getElementById("exportFile").disabled = true;
            document.getElementById('file').addEventListener('change', function (e) {
                var files = e.target.files;
                if (files.length == 0) return;
                var f = files[0];
                if (!/\.xlsx$/g.test(f.name)) {
                    alert('仅支持读取xlsx格式!');
                    return;
                }
                _file = f;
                readFile(f);
            });
        });

        function dateFormat(fmt, date) {
            let ret;
            const opt = {
                "Y+": date.getFullYear().toString(),        // 年
                "m+": (date.getMonth() + 1).toString(),     // 月
                "d+": date.getDate().toString(),            // 日
                "H+": date.getHours().toString(),           // 时
                "M+": date.getMinutes().toString(),         // 分
                "S+": date.getSeconds().toString()          // 秒
                // 有其他格式化字符需求可以继续添加,必须转化成字符串
            };
            for (let k in opt) {
                ret = new RegExp("(" + k + ")").exec(fmt);
                if (ret) {
                    fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
                };
            };
            return fmt;
        }

        function exportExcel() {
            document.getElementById("exportFile").disabled = true;
            document.getElementById("exportFile").value = "导出中...";
            // $('#exportFile').val("导出中...");
            //清理非套餐和项目数据
            let result;
            try {
                result = delNotIncludeRows();
            } catch (err) {
                alert(err);
                result = "cancel";
            }
            if (result === "cancel") {
                document.getElementById("exportFile").value = "导出权限点制品";
                document.getElementById("exportFile").disabled = false;
                return;
            }

            //删除无用的列
            delCols();
            var blob = sheet2blob();
            var version = _file.name.match(/[\d\.]+/);
            if (version && version.length > 0) {
                version = version[0]
            } else {
                version = "";
            }
            openDownloadDialog(blob, dateFormat("YYYYmmddHHMMSS", new Date()) + '-v' + version + '-企业云-' + selectedProject + '-套餐' + selectedTaocan + '-权限点制品.xlsx');

            //初始化
            readFile(_file);

        }


        //拖拽文件
        window.onload = function () {
            var oBox = document.getElementById('box');
            var timer = null;
            document.ondragover = function () {
                clearTimeout(timer);
                timer = setTimeout(function () {
                    oBox.innerHTML = '请将文件拖拽到此区域';
                    oBox.style.display = 'none';
                }, 200);
                oBox.style.display = 'block';
            };
            //进入子集的时候 会触发ondragover 频繁触发 不给ondrop机会
            oBox.ondragenter = function () {
                oBox.innerHTML = '请释放鼠标';
            };
            oBox.ondragover = function () {
                return false;
            };
            oBox.ondragleave = function () {
                oBox.innerHTML = '请将文件拖拽到此区域';
            };
            oBox.ondrop = function (ev) {
                _file = ev.dataTransfer.files[0];
                oBox.innerHTML = '读取中...';
                readFile(_file);
                return false;
            };
        };

    </script>
</body>

</html>

Last updated