記錄-html-docs-js避坑指南

来源:https://www.cnblogs.com/smileZAZ/archive/2023/04/10/17303613.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 我們公司目前在做基於tiptap的線上協同文檔,最近需要做導出 pdf、word 需求。 導出 word 文檔使用的是html-docx-js-typescript,是用 typescript 重寫了一下html-docx-js,可 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言

我們公司目前在做基於tiptap的線上協同文檔,最近需要做導出 pdf、word 需求。

導出 word 文檔使用的是html-docx-js-typescript,是用 typescript 重寫了一下html-docx-js,可以看到最近的提交記錄是 2016 年,貌似已經不維護了,很多 Issues 沒人管。

html-docs-js.png

實在找不到其他的 html 轉 word 的插件,最後只能使用它來處理,我把我在使用過程中遇到的問題一一列出來,就有了這篇避坑指南。

使用說明

  • 安裝

    安裝html-docx-js-typescript,同時安裝FileSaver用於瀏覽器端保存文件。

npm install html-docx-js-typescript file-saver --save-dev
npm install @types/html-docx-js @types/file-saver --dev

使用過程遇到的問題及處理方案

字體加粗不生效、字體背景顏色不生效處理

字體加粗<strong>和標記文本元素<mark>標簽需要替換為<b><span>標簽

const innerHtml = cloneEle.innerHTML
  // strong在word中不生效問題
  .replace(/<strong>/g, '<b>')
  .replace(/<\/strong>/g, '</b>')
  // 背景色不生效問題
  .replace(/<mark/g, '<span')
  .replace(/<\/mark>/g, '</span>')

h1 - h6 標題高度優化及未同步 word 文檔標題

我們文檔中的標題對應的 HTML 內容長這樣

 

 

 需要將內容轉換為類似<h1>xxx</h1>這樣,不然 word 中編輯時不能對應標題,修改如下:

// 標題高度和字體失效 需要設置lineHeight和fontWeight
const handleLevelStyle = (cloneEle: HTMLElement) => {
  Array.from({ length: 6 }).forEach((_, index) =>
    (cloneEle.querySelectorAll(`h${index + 1}`) as unknown as HTMLElement[]).forEach((h) => {
      h.innerText = (h.children[0] as HTMLElement).innerText
      h.style.fontSize = ''
    })
  )
}

圖片下多出一個白框

Prosemiror-images上傳圖片後,會在圖片後面生成.ProseMirror-separator這個標簽,我們在導出時只需要刪除它即可。

const removeWhiteBox = (cloneEle: HTMLElement) => {
  const separators: NodeListOf<Element> = cloneEle.querySelectorAll(
    '.ProseMirror-separator'
  )
  separators.forEach((separator) =>
    separator.parentElement?.removeChild(separator)
  )
}

列表 ul、ol

在開始處理之前,先介紹一個插入 DOM 的 API insertAdjacentElement

在 vue、react 這些框架的盛行,基本上我們已經不會再用到 DOM 操作,不過可以瞭解一下,萬一以後用得到呢。

// 將給定元素element插入到調用的元素的某個位置
element.insertAdjacentElement(position, element)

參數position可以是以下位置

  • 'beforebegin': 插入元素之前,類似 insertBefore
  • 'afterbegin': 插入元素第一個 children 之前,類似 prepend
  • 'beforeend': 插入元素最後一個 children 之後,類似 appendChild
  • 'afterend': 插入元素之後,類似 insertAfter

接著我們看一下列表這部分的修改,由於我們項目功能上的需求,列表是使用 div 標簽來改造的,所以需要將 div 標簽轉為 ul/ol,下麵是我的實現

const changeDiv2Ul = (div: HTMLElement | Element, parent?: HTMLElement | Element) => {
  const kind = div.getAttribute('data-list-kind')
  const ul = kind === 'ordered' ? document.createElement('ol') : document.createElement('ul')
  const li = document.createElement('li')
  // 去除margin 不然在word中會偏移
  !parent && (ul.style.margin = '0')
  li.innerHTML = div.innerHTML
  ul.appendChild(li)
  parent ? parent.insertAdjacentElement('afterend', ul) : div.insertAdjacentElement('afterend', ul)
  div.parentElement?.removeChild(div)

  li.querySelectorAll('.list-marker').forEach((marker) => marker.parentElement?.removeChild(marker))

  // 內容區域
  li.querySelectorAll('.list-content').forEach((content) => {
    const span = document.createElement('span')
    span.innerHTML = (content.firstChild as HTMLElement).innerHTML
    content.insertAdjacentElement('beforebegin', span)
    if (content.querySelectorAll('.prosemirror-flat-list').length) {
      content.querySelectorAll('.prosemirror-flat-list').forEach((div) => changeDiv2Ul(div, content))
    }
    content.parentElement?.removeChild(content)
  })
}
cloneEle.querySelectorAll('.prosemirror-flat-list').forEach((div) => changeDiv2Ul(div))

覆選框 checkbox

覆選框 checkbox 的處理,首先考慮的是轉為<input type='checkbox' />來處理,結果轉完後並沒有顯示覆選框;

接著又想著用 span 標簽生成一個方框,<span style='width: 16px;height: 16px...' />,這樣總能顯示了吧!結果依然不行。

正當我想不到辦法的時候,突然靈機一動,可不可以把 word 轉成 html 後看看 checkbox 最終會顯示成啥樣呢?

於是通過線上 word 轉 html將 word 轉為 html 後,看到覆選框對應的 html 內容為<span style="color:#333333; font-family:'Wingdings 2'; font-size:11pt"></span>,改一下吧。

const span = document.createElement('span')
span.innerHTML = `<span style="color:#333333; font-family:'Wingdings 2'; font-size:11pt"></span>`
marker.insertAdjacentElement('beforebegin', span)
marker.parentElement?.removeChild(marker)

轉成 word 後,覆選框的選中和取消功能也能正常使用。

附件導出、多維表等 iframe 內容

參考了一下釘釘文檔

這樣就很好改了,只需要把附件對應的節點內容,改為鏈接即可。

cloneEle.querySelectorAll('.attachment-node-wrap').forEach((attach) => {
  const title = `請至One文檔查看附件《${attach.getAttribute('name')}》`
  const anchorId = attach.parentElement?.getAttribute('data-id')
  const a = document.createElement('a')
  a.target = '_blank'
  a.href = `${location.href}&anchor=${anchorId}`
  a.innerHTML = `<span>${title}</span>`

  attach.insertAdjacentElement('beforebegin', a)
  attach.parentElement?.removeChild(attach)
})

未解決的部分

  • 表情無法導出,這個我看了下其他線上協作文檔,也有同樣的問題。

小結

其實,處理這些問題的方式也是很簡單,因為html-docs-js是用html字元串來作為導出文檔的輸入。如果導出後發現樣式不對的情況時,我們只需要去修改html內容即可。

如果有遇到像覆選框checkbox這類不知道怎麼解決的問題,也可以採用反推,先通過word轉html,然後看轉為html後的內容,再去修改需要導出的html內容,這也不失為一種解決問題的方式。

以上是我在使用html-docs-js插件時遇到的一些問題及處理方式,如果有遇到同樣問題的小伙伴,可以說下你們的處理方式。或者這裡沒有提到的問題,也歡迎大家補充。

本文轉載於:

https://juejin.cn/post/7220244579671916604

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 鎖屏面試題百日百刷,每個工作日堅持更新面試題。請看到最後就能獲取你想要的,接下來的是今日的面試題: 1.解釋一下布隆過濾器原理 在日常生活中,包括在設計電腦軟體時,我們經常要判斷一個元素是否在一個集合中。比如在字處理軟體中,需要檢查一個英語單詞是否拼寫正確(也就是要判斷它是否在已知的字典中);在 ...
  • 函數介紹:substring() 函數用於截取字元串,可從字元串的某一位置開始,向右截取若幹個字元,返回一個特定長度的字元串 功能:返回字元、二進位、文本或圖像表達式的一部分 語法:SUBSTRING ( expression, start, length ) SQL 中的 substring 函數 ...
  • 摘要:Redis事務包含兩種模式:事務模式和Lua腳本。 本文分享自華為雲社區《一文講透 Redis 事務》,作者: 勇哥java實戰分享。 準確的講,Redis事務包含兩種模式:事務模式和Lua腳本。 先說結論: Redis的事務模式具備如下特點: 保證隔離性; 無法保證持久性; 具備了一定的原子 ...
  • pg Extended Query PostgreSQL: Documentation: 15: 55.2. Message Flow 多個階段,可復用 Parse → DESCRIBE statement → SYNC Parse 解析, 將 sql 文本字元串,解析成 named prepare ...
  • 摘要:相比於傳統的微服務架構,雲原生和 serverless 技術更加靈活、高效,能夠更好地滿足用戶的需求。 本文分享自華為雲社區《《鳳凰架構》學習和思考——雲原生時代的服務架構演進史》,作者:breakDawn。 隨著雲原生的概念越來越火,服務的架構應該如何發展和演進,成為很多程式員關心的話題。大 ...
  • Q1:==springboot項目,如何使用elasticsearch的api增刪改查?查詢中有哪些方式,如果模糊查詢、排序查詢、分頁查詢?分別闡述下這些查詢方式的用法?最後舉一個完整的例子== 答: 在Spring Boot項目中使用Elasticsearch的API增刪改查,需要引入spring ...
  • GreatSQL社區月報 | 2023.03 GreatSQL 是一個開源的 MySQL 技術路線資料庫社區,社區致力於通過開放的社區合作,構建國內自主 MySQL 版本及開源資料庫技術,推動中國開源資料庫及應用生態繁榮發展。 為了幫助社區的小伙伴們更好地瞭解 GreatSQL 社區的實時進展,我們 ...
  • 前端工程化實戰是指通過組織工作流程、使用工具和技術來提高前端開發效率和質量的一種方法。常見的前端工程化實踐包括模塊化開發、自動化構建、代碼檢查和測試、性能優化等。下麵將簡要介紹模塊化開發、性能優化和組件化實踐。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...