fix(web-admin): polyfill Buffer/global for contract Word export in Vite

html-to-docx still reads Node globals after Vite rebundles; inject buffer polyfill, map global to globalThis, and alias the package to the browser build.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-09-05 15:06:20 +08:00
1 parent 8bbc716d17
commit f872333df6
5 files changed
+58 -5

No files matched your search

+12
View File
@@ -17,6 +17,18 @@ declare module 'vue-img-cutter'
declare module 'file-saver'
declare module 'buffer'
declare module '@turbodocx/html-to-docx' {
const HTMLtoDOCX: (
htmlString: string,
headerHTMLString?: string | null,
documentOptions?: Record<string, unknown>,
footerHTMLString?: string | null
) => Promise<Blob | ArrayBuffer>
export default HTMLtoDOCX
}
declare module '@turbodocx/html-to-docx/dist/html-to-docx.browser.esm.js' {
const HTMLtoDOCX: (
htmlString: string,
@@ -1,5 +1,7 @@
/** 合同打印弹窗:HTML → DOCX 预处理与生成 */
import { Buffer } from 'buffer'
const DOCX_MIME = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
type HtmlToDocxFn = (
@@ -9,13 +11,30 @@ type HtmlToDocxFn = (
footerHTMLString?: string | null
) => Promise<Blob | ArrayBuffer>
/**
* html-to-docx(含 browser 包)在 Vite 二次打包后仍会直接读全局 Buffer/global。
* 必须在调用转换前注入,否则 buildImage 报 Buffer is not defined,随后 global is not defined。
*/
function ensureDocxNodeGlobals(): void {
const g = globalThis as typeof globalThis & {
Buffer?: typeof Buffer
global?: typeof globalThis
process?: { env?: Record<string, string | undefined> }
}
if (!g.Buffer) g.Buffer = Buffer
if (!g.global) g.global = g
if (!g.process) g.process = { env: {} }
else if (!g.process.env) g.process.env = {}
}
/**
* 懒加载 browser 构建。
* Vite 默认会解析到 Node ESM(依赖 Buffer/global),浏览器会报错;
* 顶层静态 import browser 包又会让 Node 单测因 XMLHttpRequest 初始化失败。
* Vite 默认会解析到 Node ESM(依赖 Buffer/global);显式 browser 入口 + 上方 globals 注入。
*/
async function loadHtmlToDocx(): Promise<HtmlToDocxFn> {
const mod = await import('@turbodocx/html-to-docx/dist/html-to-docx.browser.esm.js')
ensureDocxNodeGlobals()
// 经 vite alias 指向 browser.esm.js
const mod = await import('@turbodocx/html-to-docx')
const candidate = typeof mod === 'function' ? mod : (mod as { default?: unknown }).default
if (typeof candidate !== 'function') {
throw new Error('html-to-docx 加载失败:未获得可调用的转换函数')
@@ -84,9 +103,11 @@ export async function generateContractDocxBlob(
preparedHtml: string,
title?: string
): Promise<Blob> {
ensureDocxNodeGlobals()
const HTMLtoDOCX = await loadHtmlToDocx()
const htmlWithImages = await inlineRemoteImagesAsDataUrls(preparedHtml)
const fullHtml = `<!DOCTYPE html><html><head><meta charset="UTF-8" /></head><body>${htmlWithImages}</body></html>`
ensureDocxNodeGlobals()
const result = await HTMLtoDOCX(fullHtml, null, {
orientation: 'portrait',
title: title || '合同',