原生js请求数据生成表格并导出csv文件(可以转化为xls文件,也就是wps文件,兼容各种版本浏览器包括可恶的ie)

174 阅读1分钟

闲话少说直接上效果

导出的文件效果如下

image.png

在浏览器上的效果如下

image.png

直接上完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>请求数据生成表格并导出到xls/csv文件等</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        height: 100%;
      }
      .tableDom {
        margin: 15px 15px;
        display: flex;
        flex-direction: column;
      }

      .tableDom .btn_content button {
        font-size: 14px;
        border: none;
        padding: 3px 5px;
        background-color: #197bfe;
        color: #fafafa;
        border-radius: 5px;
        cursor: pointer;
      }
      .tableDom .btn_content button:hover {
        box-shadow: 0 0 5px #197bfe;
      }

      table {
        height: auto;
        margin: 5px 0;
        border-collapse: collapse;
        line-height: 25px;
        border-spacing: 0px;
        /* border-bottom: 1px solid #c1c1c1; */
      }

      table th,
      table td {
        padding: 0 5px;
        color: #333;
        text-align: center;
        border: 1px solid #c1c1c1;
        font-size: 14px;
      }

      /* 滚动条宽度 */
      ::-webkit-scrollbar {
        width: 4px;
        height: 4px;
        background-color: transparent;
      }

      /* 滚动条颜色 */
      ::-webkit-scrollbar-thumb {
        background-color: #a8a8a8;
      }

      table thead {
        color: white;
        background-color: #efefef;
      }

      table thead th {
        border-bottom: none;
      }

      table tbody {
        display: block;
        /* width: calc(100%); */
        /*这里的8px是滚动条的宽度*/
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
      }

      table thead tr,
      table tbody tr {
        box-sizing: border-box;
        table-layout: fixed;
        display: table;
        width: 100%;
      }

      table tbody tr:nth-of-type(odd) {
        background: #ffffff;
      }

      table tbody tr:nth-of-type(even) {
        background: #e0e0e0;
      }

      table tbody tr td {
        border-bottom: none;
      }
      table body {
        border-bottom: 1px solid #c1c1c1 !important;
      }
    </style>
  </head>
  <body>
    <div class="tableDom">
      <div class="btn_content">
        <button id="daochu">导出数据</button>
        <button id="shuaxin">刷新数据</button>
      </div>
      <table id="sgs_table">
        <thead id="sgsthead"></thead>
        <tbody id="sgstbody"></tbody>
      </table>
    </div>
  </body>

  <script>
    // 声明导出csv方法
    // 当页面渲染完成再执行
    window.onload = function () {
      // 获取两个按钮和表格标签
      const exportBtn = document.getElementById("daochu"); // 导出按钮
      const refreshBtn = document.getElementById("shuaxin"); // 刷新按钮
      const showThead = document.getElementById("sgsthead"); // 获取table的thead标签
      const showTbody = document.getElementById("sgstbody"); // 获取table的tbody标签

      // 声明空盒子用于承载获取出来的数据
      var data = null;

      // 模拟请求获取数据 里面的代码可以根据自己的业务拆出来用
      setTimeout(function () {
        // 我的后端给我返回的数据也是这样以中文作为对象的键值的,当然,每个人遇到的后端都不一样,看个人吧
        let getData = [
          { 姓名: "新氦锂铍硼", 语文: "110", 数学: "99", 英语: "104" },
          { 姓名: "碳氮氧氟氖", 语文: "99", 数学: "115", 英语: "98" },
          { 姓名: "钠镁铝硅灵", 语文: "120", 数学: "102", 英语: "14" },
          { 姓名: "流氯氩钾钙", 语文: "88", 数学: "66", 英语: "39" },
        ];
        data = getData; // 将数据存到盒子中,在后续导出csv文件的时候需要用到,数据的键值就是姓名,语文数学英语这些,也是导出后的表格的表头

        // 当数据请求回来之后执行下面操作

        // 这里用于判断获取回来的数据是否是数组而且数组长度是否为0
        if (
          Object.prototype.toString.call(getData) === "[object Array]" &&
          getData.length > 0
        ) {
          let newTheadTH = []; // 声明一个空数组,承载表头对象,此处就是获取渲染页面上的表头数据
          for (let key in getData[0]) {
            newTheadTH.push({
              title: key,
              widths: 150, // 这是给页面上的表格设定宽度用的,当然这里目前用不上,我就不改了,后面不需要这个可以自行删除,不影响的
            });
          }

          // 以上就获取到了表格头部了,然后先渲染
          let newHtmlTh = "";
          newTheadTH.forEach(function (item, index) {
            newHtmlTh = newHtmlTh.concat(
              '<th width="' + item.widths + '">' + item.title + "</th>"
            );
          });

          // 生成头标签
          showThead.innerHTML = "<tr>" + newHtmlTh + "</tr>";

          // 生成完头部标签就要生成tbody的数据内容了
          let trTag = ""; // 创建空字符串承载 tr

          // 开始生成tbody的所有内容
          getData.forEach(function (item, index) {
            let forInKey = "";
            for (let key in item) {
              forInKey = forInKey.concat(
                '<td width="150">' + item[key] + "</td>"
              );
            }
            trTag = trTag.concat("<tr>" + forInKey + "</tr>");
          });
          showTbody.innerHTML = trTag;
        }
        // 如果请求回来的数据不为数组或者是数组但是长度为0的时候 判空
        else {
          showThead.innerHTML = "<tr><td>请求数据为空</td></tr>";
        }
      }, 1000);

      // 刷新按钮我的方法我就不写了,就是将setTimeout内部的代码重新执行一遍
      refreshBtn.addEventListener("click", function () {
        // 刷新的逻辑
      });

      // 导出按钮*****主要的重点
      exportBtn.addEventListener("click", function () {
        
        let csvContent = Object.keys(data[0]).join(",") + "\n";  // 生成导导出表格的头部
        for (let i = 0; i < data.length; i++) {
          let row = [];
          for (let key in data[i]) {
            if (data[i].hasOwnProperty(key)) {
              row.push(data[i][key]);
            }
          }
          csvContent += row.join(",") + "\n";
        }

        // return
        // 创建一个Blob对象
        const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8" });

        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
          console.log("navigator:", navigator);
          navigator.msSaveBlob(blob, "导出.csv");
        } else {
          const link = document.createElement("a");
          link.href = window.URL.createObjectURL(blob);
          link.download = "导出.csv";

          // 将a标签添加到DOM中,模拟点击下载链接,最后移除a标签
          link.style.display = "none";
          document.body.appendChild(link);
          // console.log(link);
          link.click();

          document.body.removeChild(link);
        }
      });
    };
  </script>
</html>

然后我解析导出csv文件那个方法,因为其他方法一看就懂

首先我们要知道csv文件格式是什么样的,直接看对应图就好啦, 红色指向表格的表头,在csv文件的第一行,注意,第一行是表头,每一列用一个英文逗号分隔,

蓝色指向是表数据,也是逗号分隔的,注意csv文件中是表头是不变的,也就是说,从csv文件的第二行开始是每一行的数据,可能我说的不是很清除,直接看图理解快一些

image.png

在导出方法中的以下这一块代码说的就是先把csv文件的头部存好,然后从第二行开始每一行都是一列数据,我用 /n 就是再强制转行,就是为了达到我需要的数据格式

        for (let i = 0; i < data.length; i++) {
          let row = [];
          for (let key in data[i]) {
            if (data[i].hasOwnProperty(key)) {
              row.push(data[i][key]);
            }
          }
          csvContent += row.join(",") + "\n";
        }

然后下面的判断就是看看浏览是什么样的,就使用哪一种方法导出csv文件,也就是wps文件

          console.log("navigator:", navigator); // 打印是什么牛马浏览器
          navigator.msSaveBlob(blob, "导出.csv");
        } else {
          const link = document.createElement("a");
          link.href = window.URL.createObjectURL(blob);
          link.download = "导出.csv";

          // 将a标签添加到DOM中,模拟点击下载链接,最后移除a标签
          link.style.display = "none";
          document.body.appendChild(link);
          // console.log(link);
          link.click();

          document.body.removeChild(link);
        }