【分享】将 WPS 中的表格转换成笔记中的表格(处理合并单元格)

本贴最后更新于 371 天前,其中的信息可能已经事过景迁

简介

主要是为了解决从 Excel 或者 WPS 中复制表格粘贴到笔记中后,因合并单元格没处理好导致表格数据混乱的问题。

之前是用表格挂件的方式来解决,但是感觉使用挂件来放表格数据的话,表格的内容跟笔记内容割裂开了,在全局搜索的时候,表格中命中的内容无法作为搜索结果来呈现。

所以这次换个方式:将从 Excel 或者 WPS 中复制的表格转成笔记支持的表格并插入到笔记中,让表格内容成为笔记正文。

实现思路

image20230322114319c2204b1.png

使用方法

  1. 设置(Alt+P)——外观——代码片段——设置——JS中添加并启用代码片段,启用后,笔记右上角多出一个 表格转换 的图标
  2. 在 WPS 中选中表格并复制,粘贴到 LuckSheet 表格挂件中;
  3. 粘贴到挂件后,表格还是选中状态,此时按Ctrl+C再次复制;
  4. 点击右上角的【表格转换】图标,提示成功后,对应表格模板字符串已经复制到剪切板;
  5. 到笔记中按Ctrl+V粘贴表格。

**注意:**第二步在 WPS 中选中表格时,表头(也就是第一行)不要有行合并(多行合并),这种情况还未进行处理。

如果你的表格第一行就有行合并,可以往上多选一行空单元格作为表头以免出现异常。

视频演示

演示环境:V2.7.2 Windows 10

表格挂件压缩包

表格.zip

备注:挂件中全屏按钮要生效的话,需要到主题的 theme.css 中添加一个样式:

.fullScreen2 {
    position: fixed;
    top: 30px;
    left: 0;
    width: 100vw !important;
    height: 95vh !important;
    z-index: 200;
}

代码片段

(() => {
  // 请求函数
  function request(url, data = null, method = "POST") {
    return new Promise((resolve, reject) => {
      if (method.toUpperCase() == "POST") {
        fetch(url, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(data),
        })
          .then(
            (data) => resolve(data.json()),
            (error) => {
              reject(error);
            }
          )
          .catch((err) => {
            console.error("请求失败:", err);
          });
      }
    });
  }
  // 复制内容到剪切板
  function patseIntoClipboard(txt) {
    let status = false;
    const input = document.createElement("textarea");
    document.body.appendChild(input);
    input.innerHTML = txt;
    input.select();
    if (document.execCommand("copy")) {
      document.execCommand("copy");
      status = true;
      showMessage("表格内容已复制到剪切板");
    } else {
      showMessage("复制失败");
    }
    document.body.removeChild(input);
    return status;
  }

  // 弹出提示信息
  async function showMessage(msg, timeout = 2000) {
    await request("/api/notification/pushMsg", { msg, timeout });
  }

  // 表格转换
  async function convertTable() {
    let tableStr = await navigator.clipboard.readText();
    let parser = new DOMParser();
    let doc = parser.parseFromString(tableStr, "text/html");
    let tableDom = doc?.querySelector("table");
    let trs = tableDom?.querySelectorAll("tr");
    let status = doc && tableDom && trs;
    if (!status) {
      showMessage("操作失败");
      return;
    }
    // 获取表格的行数
    let rowNum = trs.length;

    // 获取表格的列数
    let firstRow = trs[0].querySelectorAll("td");
    let colNum = firstRow.length;
    for (let td of firstRow) {
      if (td.getAttribute("colspan")) {
        let colspan = td.getAttribute("colspan");
        colNum += parseInt(colspan) - 1;
      }
    }

    // 创建一个二维数组,用来放置表格的数据
    let tableData = [];
    for (let i = 0; i < rowNum; i++) {
      let tempArr = new Array(colNum).fill(null);
      tableData.push(tempArr);
    }

    // 索引差值
    let gap = 0;
    // 被合并的单元格——填充此字符串
    let emptyData = `{: class="fn__none"}`;

    // 逐行解析表格,得到二维数组
    for (let [rowIndex, tr] of trs.entries()) {
      let tds = tr.querySelectorAll("td");
      gap = 0;
      row: for (let colIndex = 0; colIndex < tds.length; colIndex++) {
        let td = tds[colIndex];
        let rowspan = td.getAttribute("rowspan");
        let colspan = td.getAttribute("colspan");
        let content = td.innerText;
        while (tableData[rowIndex][colIndex + gap] == emptyData) {
          gap++;
        }

        if (!rowspan && !colspan) {
          tableData[rowIndex][colIndex + gap] = content;
        } else {
          rowspan = parseInt(rowspan);
          colspan = parseInt(colspan);
          let a = new Array(colspan).fill(emptyData);
          for (let i = 0; i < rowspan; i++) {
            tableData[rowIndex + i].splice(colIndex + gap, colspan, ...a);
          }
          tableData[rowIndex][colIndex + gap] =
            content + `{: colspan="${colspan}" rowspan="${rowspan}"}`;

          gap = gap + colspan - 1;
        }
      }
    }

    // 将数组中的数据组合成表格模板
    let templateStr = "";
    for (let [index, row] of tableData.entries()) {
      templateStr += "|";
      for (let item of row) {
        templateStr += item + "|";
      }
      templateStr += "\n";
      if (index === 0) {
        templateStr = templateStr + "|" + ":-------: |".repeat(colNum) + "\n";
      }
    }
    // 将表格模板对应字符串写入到剪切板
    patseIntoClipboard(templateStr);
  }

  // 顶栏添加一个按钮
  const barMode = document.getElementById("barMode");
  barMode.insertAdjacentHTML(
    "beforebegin",
    '<div id="convertTable" class="toolbar__item b3-tooltips b3-tooltips__se" aria-label="表格转换" ></div>'
  );
  const convertBtn = document.getElementById("convertTable");
  convertBtn.style.width = "auto";
  convertBtn.innerHTML = `<svg t="1679417318794" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2816" width="32" height="32"><path d="M885.3 957.3H140.7c-42.2 0-76.5-34.3-76.5-76.5V136.3c0-42.2 34.3-76.5 76.5-76.5h744.6c42.2 0 76.5 34.3 76.5 76.5v744.6c-0.1 42.1-34.3 76.4-76.5 76.4z m-744.6-851c-16.5 0-29.9 13.4-29.9 29.9v744.6c0 16.5 13.4 29.9 29.9 29.9h744.6c16.5 0 29.9-13.4 29.9-29.9V136.3c0-16.5-13.4-29.9-29.9-29.9H140.7z" fill="#7D7D7D" p-id="2817"></path><path d="M938.5 315.8h-851V91.1c0-4.4 3.6-8 8-8h835c4.4 0 8 3.6 8 8v224.7z" fill="#CBCBCB" p-id="2818"></path><path d="M961.7 339H64.3V136.3c0-42.2 34.3-76.5 76.5-76.5h744.6c42.2 0 76.5 34.3 76.5 76.5V339z m-850.9-46.5h804.4V136.3c0-16.5-13.4-29.9-29.9-29.9H140.7c-16.5 0-29.9 13.4-29.9 29.9v156.2zM938.5 644.8h-851c-12.9 0-23.3-10.4-23.3-23.3 0-12.8 10.4-23.3 23.3-23.3h851c12.9 0 23.3 10.4 23.3 23.3-0.1 12.9-10.5 23.3-23.3 23.3z" fill="#7D7D7D" p-id="2819"></path><path d="M363.4 957.3c-12.9 0-23.3-10.4-23.3-23.3V315.8c0-12.8 10.4-23.3 23.3-23.3s23.3 10.4 23.3 23.3V934c0 12.9-10.4 23.3-23.3 23.3zM662.6 957.3c-12.9 0-23.3-10.4-23.3-23.3V315.8c0-12.8 10.4-23.3 23.3-23.3s23.3 10.4 23.3 23.3V934c0 12.9-10.5 23.3-23.3 23.3z" fill="#7D7D7D" p-id="2820"></path></svg>`;
  convertBtn.addEventListener(
    "click",
    (e) => {
      convertTable();
      e.stopPropagation();
      e.preventDefault();
    },
    true
  );
})();
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    18131 引用 • 66885 回帖
1 操作
someone69799 在 2023-03-23 21:07:29 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • zuoez02 1 评论

    欢迎使用插件系统来分享你的代码片段,更好的留存你的代码,并版本化你的实现~

    https://ld246.com/article/1677683841865

    还没研究过插件具体怎么写,到时候我去看看示例是咋样的。
    someone69799
  • 其他回帖
  • lx233

    请问一下 表格挂件压缩包 放在了挂件目录 E:\xxxxxx\SiYuan\data\widgets 没有生效,已下载挂件里也看不到,重启也没用,解压目录中英文也试了,还需要其他的设置吗?

    2 回复
  • someone69799
    作者

    这个不错,之前没留意到有这个特性,看来思源有不少功能被我漏掉了 😂 。

  • lx233

    解决了,我是 2.8.0 版本一定要加 widget.json,里面的 "name" 要和文件夹名对应才行,另外挂件 已下载 里也不显示,但是 /挂件 就有选项了

  • 查看全部回帖