exportWord.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // @ts-ignore
  2. import JSZipUtils from 'jszip-utils'
  3. import docxtemplater from 'docxtemplater'
  4. // @ts-ignore
  5. import { saveAs } from 'file-saver'
  6. import PizZip from 'pizzip'
  7. export const getBase64Sync = (imgUrl: string) => {
  8. return new Promise<string>(function (resolve, reject) {
  9. // 一定要设置为let,不然图片不显示
  10. let image = new Image()
  11. image.crossOrigin = 'anonymous'
  12. image.src = imgUrl
  13. image.onload = function () {
  14. let canvas = document.createElement('canvas')
  15. canvas.width = image.width
  16. canvas.height = image.height
  17. let context = canvas.getContext('2d')
  18. context?.drawImage(image, 0, 0, image.width, image.height)
  19. //图片后缀名
  20. let ext = image.src.substring(image.src.lastIndexOf('.') + 1).toLowerCase()
  21. let quality = 0.8
  22. let dataurl = canvas.toDataURL('image/' + ext, quality)
  23. resolve(dataurl)
  24. }
  25. image.onerror = function (err) {
  26. reject(err)
  27. }
  28. })
  29. }
  30. export const base64DataURLToArrayBuffer = (dataURL: string) => {
  31. const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/
  32. if (!base64Regex.test(dataURL)) {
  33. return false
  34. }
  35. const stringBase64 = dataURL.replace(base64Regex, '')
  36. let binaryString
  37. if (typeof window !== 'undefined') {
  38. binaryString = window.atob(stringBase64)
  39. } else {
  40. binaryString = Buffer.from(stringBase64, 'base64').toString('binary')
  41. }
  42. const len = binaryString.length
  43. const bytes = new Uint8Array(len)
  44. for (let i = 0; i < len; i++) {
  45. const ascii = binaryString.charCodeAt(i)
  46. bytes[i] = ascii
  47. }
  48. return bytes.buffer
  49. }
  50. export const exportWordDocx = async (
  51. url: string,
  52. docxData: Record<string, any>,
  53. fileName: string
  54. ) => {
  55. const ImageModule = require('docxtemplater-image-module-free')
  56. JSZipUtils.getBinaryContent(url, async function (error: unknown, content: any) {
  57. if (error) {
  58. throw error
  59. }
  60. // 创建一个PizZip实例,内容为模板的内容
  61. let zip = new PizZip(content)
  62. // 创建并加载docxtemplater实例对象
  63. let doc = new docxtemplater().loadZip(zip)
  64. doc.attachModule(
  65. new ImageModule({
  66. getImage: (base64: string) => base64DataURLToArrayBuffer(base64),
  67. getSize: (buffer: string, base64: string, key: string) => {
  68. if (docxData.imagePages) {
  69. for (const row of docxData.imagePages) {
  70. for (const col of row) {
  71. for (const img of col) {
  72. if (img.img === base64) {
  73. return [img.width, img.height]
  74. }
  75. }
  76. }
  77. }
  78. }
  79. return [70, 70]
  80. }
  81. })
  82. )
  83. // 去除未定义值所显示的undefined
  84. doc.setOptions({
  85. nullGetter: function () {
  86. return ''
  87. }
  88. }) // 设置角度解析器Could not find a declaration file for module 'jszip-utils'.
  89. doc.setData({
  90. ...docxData
  91. })
  92. try {
  93. // 用模板变量的值替换所有模板变量
  94. doc.render()
  95. } catch (error: any) {
  96. let e = {
  97. message: error.message,
  98. name: error.name,
  99. stack: error.stack,
  100. properties: error.properties
  101. }
  102. console.log(JSON.stringify({ error: e }))
  103. throw error
  104. }
  105. // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
  106. let out = doc.getZip().generate({
  107. type: 'blob',
  108. mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  109. })
  110. // 将目标文件对象保存为目标类型的文件,并命名
  111. saveAs(out, fileName)
  112. })
  113. }