記錄-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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...