exportdocx.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import Color from 'color'
  2. import {
  3. IElement,
  4. ElementType,
  5. TitleLevel,
  6. ListStyle,
  7. Command
  8. } from '@hufe921/canvas-editor'
  9. import {
  10. Document,
  11. Packer,
  12. Paragraph,
  13. Header,
  14. Footer,
  15. Table,
  16. HeadingLevel,
  17. ParagraphChild,
  18. TextRun,
  19. Tab,
  20. ExternalHyperlink,
  21. ImageRun,
  22. WidthType,
  23. TableRow,
  24. TableCell,
  25. MathRun
  26. } from 'docx'
  27. import { saveAs } from './utils'
  28. // 标题映射
  29. const titleLevelToHeadingLevel = {
  30. [TitleLevel.FIRST]: HeadingLevel.HEADING_1,
  31. [TitleLevel.SECOND]: HeadingLevel.HEADING_2,
  32. [TitleLevel.THIRD]: HeadingLevel.HEADING_3,
  33. [TitleLevel.FOURTH]: HeadingLevel.HEADING_4,
  34. [TitleLevel.FIFTH]: HeadingLevel.HEADING_5,
  35. [TitleLevel.SIXTH]: HeadingLevel.HEADING_6
  36. }
  37. function convertElementToParagraphChild(element) {
  38. if (element.type === ElementType.IMAGE) {
  39. return new ImageRun({
  40. data: element.value,
  41. transformation: {
  42. width: element.width||'',
  43. height: element.height||''
  44. }
  45. })
  46. }
  47. if (element.type === ElementType.HYPERLINK) {
  48. return new ExternalHyperlink({
  49. children: [
  50. new TextRun({
  51. text: element.valueList?.map(child => child.value).join(''),
  52. style: 'Hyperlink'
  53. })
  54. ],
  55. link: element.url||''
  56. })
  57. }
  58. if (element.type === ElementType.TAB) {
  59. return new TextRun({
  60. children: [new Tab()]
  61. })
  62. }
  63. if (element.type === ElementType.LATEX) {
  64. return new MathRun(element.value)
  65. }
  66. return new TextRun({
  67. font: element.font,
  68. text: element.value,
  69. bold: element.bold,
  70. size: `${(element.size || 16) / 0.75}pt`,
  71. color: Color(element.color).hex() || '#000000',
  72. italics: element.italic,
  73. strike: element.strikeout,
  74. highlight: element.highlight ? Color(element.highlight).hex() : undefined,
  75. superScript: element.type === ElementType.SUPERSCRIPT,
  76. subScript: element.type === ElementType.SUBSCRIPT,
  77. underline: element.underline ? {} : undefined
  78. })
  79. }
  80. function convertElementListToDocxChildren(
  81. elementList
  82. ) {
  83. const children = []
  84. let paragraphChild = []
  85. function appendParagraph() {
  86. if (paragraphChild.length) {
  87. children.push(
  88. new Paragraph({
  89. children: paragraphChild
  90. })
  91. )
  92. paragraphChild = []
  93. }
  94. }
  95. for (let e = 0; e < elementList.length; e++) {
  96. const element = elementList[e]
  97. if (element.type === ElementType.TITLE) {
  98. appendParagraph()
  99. children.push(
  100. new Paragraph({
  101. heading: titleLevelToHeadingLevel[element.level||''],
  102. children:
  103. element.valueList?.map(child =>
  104. convertElementToParagraphChild(child)
  105. ) || []
  106. })
  107. )
  108. } else if (element.type === ElementType.LIST) {
  109. appendParagraph()
  110. // 拆分列表
  111. const listChildren =
  112. element.valueList
  113. ?.map(item => item.value)
  114. .join('')
  115. .split('\n')
  116. .map(
  117. (text, index) =>
  118. new Paragraph({
  119. children: [
  120. new TextRun({
  121. text: `${
  122. !element.listStyle ||
  123. element.listStyle === ListStyle.DECIMAL
  124. ? `${index + 1}. `
  125. : `• `
  126. }${text}`
  127. })
  128. ]
  129. })
  130. ) || []
  131. children.push(...listChildren)
  132. } else if (element.type === ElementType.TABLE) {
  133. appendParagraph()
  134. const { trList } = element
  135. const tableRowList = []
  136. for (let r = 0; r < (trList||[]).length; r++) {
  137. const tdList = (trList||[])[r].tdList
  138. const tableCellList = []
  139. for (let c = 0; c < tdList.length; c++) {
  140. const td = tdList[c]
  141. tableCellList.push(
  142. new TableCell({
  143. columnSpan: td.colspan,
  144. rowSpan: td.rowspan,
  145. children: convertElementListToDocxChildren(td.value) || []
  146. })
  147. )
  148. }
  149. tableRowList.push(
  150. new TableRow({
  151. children: tableCellList
  152. })
  153. )
  154. }
  155. children.push(
  156. new Table({
  157. rows: tableRowList,
  158. width: {
  159. size: '100%',
  160. type: WidthType.PERCENTAGE
  161. }
  162. })
  163. )
  164. } else if (element.type === ElementType.DATE) {
  165. paragraphChild.push(
  166. ...(element.valueList?.map(child =>
  167. convertElementToParagraphChild(child)
  168. ) || [])
  169. )
  170. }
  171. else {
  172. if (/^\n/.test(element.value)) {
  173. appendParagraph()
  174. element.value = element.value.replace(/^\n/, '')
  175. }
  176. paragraphChild.push(convertElementToParagraphChild(element))
  177. }
  178. }
  179. appendParagraph()
  180. return children
  181. }
  182. export default function (command) {
  183. return function (options) {
  184. const { fileName } = options
  185. const {
  186. data: { header, main, footer }
  187. } = command.getValue()
  188. console.log(convertElementListToDocxChildren(main || []))
  189. const doc = new Document({
  190. sections: [
  191. {
  192. headers: {
  193. default: new Header({
  194. children: convertElementListToDocxChildren(header || [])
  195. })
  196. },
  197. footers: {
  198. default: new Footer({
  199. children: convertElementListToDocxChildren(footer || [])
  200. })
  201. },
  202. children: convertElementListToDocxChildren(main || [])
  203. }
  204. ]
  205. })
  206. console.log(doc)
  207. return
  208. Packer.toBlob(doc).then(blob => {
  209. saveAs(blob, `${fileName}.docx`)
  210. })
  211. }
  212. }