UNPKG

vue-print-next

Version:

This is a directive wrapper for printed, Simple, fast, convenient, light.

784 lines (783 loc) 37.5 kB
var D = Object.defineProperty, F = Object.defineProperties; var q = Object.getOwnPropertyDescriptors; var S = Object.getOwnPropertySymbols; var I = Object.prototype.hasOwnProperty, L = Object.prototype.propertyIsEnumerable; var T = (l, e, t) => e in l ? D(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t, E = (l, e) => { for (var t in e || (e = {})) I.call(e, t) && T(l, t, e[t]); if (S) for (var t of S(e)) L.call(e, t) && T(l, t, e[t]); return l; }, z = (l, e) => F(l, q(e)); var H = (l, e) => { var t = {}; for (var o in l) I.call(l, o) && e.indexOf(o) < 0 && (t[o] = l[o]); if (l != null && S) for (var o of S(l)) e.indexOf(o) < 0 && L.call(l, o) && (t[o] = l[o]); return t; }; var k = (l, e, t) => T(l, typeof e != "symbol" ? e + "" : e, t); const g = "[VuePrintNext]"; let N = 0; const C = { A0: { width: "841mm", height: "1189mm" }, A1: { width: "594mm", height: "841mm" }, A2: { width: "420mm", height: "594mm" }, A3: { width: "297mm", height: "420mm" }, A4: { width: "210mm", height: "297mm" }, A5: { width: "148mm", height: "210mm" }, A6: { width: "105mm", height: "148mm" }, A7: { width: "74mm", height: "105mm" }, A8: { width: "52mm", height: "74mm" }, Letter: { width: "215.9mm", height: "279.4mm" }, Legal: { width: "215.9mm", height: "355.6mm" }, Tabloid: { width: "279.4mm", height: "431.8mm" } }, j = (l = 32) => `width: ${l}px; height: ${l}px; border: none; background: transparent; cursor: pointer; border-radius: 4px; display: flex; align-items: center; justify-content: center; transition: background-color 0.2s ease; padding: 0; outline: none;`, B = (l, e, t = 32, o = "") => { const n = document.createElement("button"); return n.setAttribute("class", l), n.setAttribute("style", j(t)), n.innerHTML = e, o && n.setAttribute("title", o), n; }, x = (l, e) => (l.addEventListener("mouseover", () => { l.style.backgroundColor = e ? "#444" : "#f0f0f0"; }), l.addEventListener("mouseout", () => { l.style.backgroundColor = "transparent"; }), l), w = { close: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>', zoomIn: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>', zoomOut: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>', darkMode: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>', lightMode: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>', fullscreen: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" y1="3" x2="14" y2="10"></line><line x1="3" y1="21" x2="10" y2="14"></line></svg>', exitFullscreen: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="8 3 3 3 3 8"></polyline><polyline points="21 8 21 3 16 3"></polyline><polyline points="3 16 3 21 8 21"></polyline><polyline points="16 21 21 21 21 16"></polyline></svg>', print: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>', paper: '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>' }, b = (l, e) => { var o; if (typeof l == "number" || !isNaN(Number(l))) return `${l}${e}`; const t = (o = l.toString().match(/[a-zA-Z]+/g)) == null ? void 0 : o[0]; return !t || !(t in C) ? `${l}mm` : l; }; function O() { try { return /^((?!chrome|android).)*safari/i.test(navigator.userAgent) && navigator.vendor === "Apple Computer, Inc."; } catch (l) { return !1; } } function U(l) { const { immediateEvents: e = ["load"], debug: t = !1 } = {}, o = HTMLIFrameElement.prototype.addEventListener; return HTMLIFrameElement.prototype.addEventListener = function(n, r, i) { var s; if (o.call(this, n, r, i), !(!O() || typeof r != "function")) try { e.includes(n) && ((s = this.contentWindow) == null ? void 0 : s.document.readyState) === "complete" && (t && console.log(`[Safari Iframe Patch] Triggering "${n}" immediately`), this.contentWindow.setTimeout(() => { r.call(this, new Event(n)); })); } catch (a) { console.warn("[Safari Iframe Patch] Error:", a); } }, function() { HTMLIFrameElement.prototype.addEventListener = o, t && console.log("[Safari Iframe Patch] Restored original addEventListener"); }; } class W { constructor(e) { // html 文档标准 k(this, "standards", { strict: "strict", loose: "loose", html5: "html5" }); // 打印窗口的 iframe id k(this, "iframeId", ""); // 预览窗口的 body k(this, "previewBody", null); // 预览窗口的 关闭按钮 k(this, "close", null); // 预览窗口的缩放比例 k(this, "scale", 1); // 预览窗口是否为深色模式 k(this, "isDarkMode", !1); // 预览窗口是否为全屏模式 k(this, "isFullscreen", !0); // 默认为全屏模式 // 用户设置 k(this, "settings"); const v = e, { vue: t, standard: o = "html5", zIndex: n = 20002, previewTitle: r = "打印预览", previewPrintBtnLabel: i = "打印", preview: s = !1, popTitle: a = document.title, previewSize: c, darkMode: h = !1, windowMode: d = !1, defaultScale: p = 1, previewTools: f = { zoom: !0, theme: !0, fullscreen: !0 } } = v, y = H(v, [ "vue", "standard", "zIndex", "previewTitle", "previewPrintBtnLabel", "preview", "popTitle", "previewSize", "darkMode", "windowMode", "defaultScale", "previewTools" ]); this.isDarkMode = h, this.isFullscreen = !d, this.scale = p, this.settings = z(E({ standard: o, zIndex: n, previewTitle: r, previewPrintBtnLabel: i, preview: s, popTitle: a, previewSize: c, darkMode: h, windowMode: d, defaultScale: p, previewTools: f }, y), { previewBeforeOpenCallback: () => { var m; return (m = e.previewBeforeOpenCallback) == null ? void 0 : m.call(e, t); }, previewOpenCallback: () => { var m; return (m = e.previewOpenCallback) == null ? void 0 : m.call(e, t); }, openCallback: () => { var m; return (m = e.openCallback) == null ? void 0 : m.call(e, t); }, closeCallback: () => { var m; return (m = e.closeCallback) == null ? void 0 : m.call(e, t); }, beforeOpenCallback: () => { var m; return (m = e.beforeOpenCallback) == null ? void 0 : m.call(e, t); } }), this.init(); } init() { this.iframeId = `printArea_${++N}`; const { el: e, url: t, asyncUrl: o } = this.settings; if (U(), e) this.handlePrintWindow(""); else if (t) this.handlePrintWindow(t); else if (o) o((n) => this.handlePrintWindow(n), this.settings.vue); else throw new Error(`${g}: Either "el", "url", or "asyncUrl" parameter must be provided in the settings.`); } handlePrintWindow(e) { const t = this.getPrintWindow(e); !e && this.write(t.doc), this.settings.preview ? this.previewIframeLoad() : this.print(t); } addEvent(e, t, o) { e && e.addEventListener(t, o, !1); } previewIframeLoad() { var o, n; const e = document.getElementById("vue-print-next-previewBox"); if (!e) return; const t = e.querySelector("iframe"); (n = (o = this.settings).previewBeforeOpenCallback) == null || n.call(o), this.addEvent(t, "load", () => { var r, i; this.previewBoxShow(), (i = (r = this.settings).previewOpenCallback) == null || i.call(r); }), this.addEvent(e.querySelector(".previewBodyUtilPrintBtn"), "click", () => { var r, i, s, a, c, h; (i = (r = this.settings).beforeOpenCallback) == null || i.call(r), t != null && t.contentWindow ? this.waitForFontsLoaded(t.contentWindow).then(() => { var d, p, f, y, v; (p = (d = this.settings).openCallback) == null || p.call(d), (f = t.contentWindow) == null || f.print(), (v = (y = this.settings).closeCallback) == null || v.call(y); }).catch(() => { var d, p, f, y, v; console.warn(`${g}: Font loading timeout in preview mode, proceeding with print`), (p = (d = this.settings).openCallback) == null || p.call(d), (f = t.contentWindow) == null || f.print(), (v = (y = this.settings).closeCallback) == null || v.call(y); }) : ((a = (s = this.settings).openCallback) == null || a.call(s), (h = (c = this.settings).closeCallback) == null || h.call(c)); }); } print(e) { var r, i; const t = document.getElementById(this.iframeId) || e.f, o = t == null ? void 0 : t.contentWindow; if (!o) return; const n = () => { this.waitForFontsLoaded(o).then(() => { const s = setTimeout(() => { var a, c, h, d; o.focus(), (c = (a = this.settings).openCallback) == null || c.call(a), o.print(), t.remove(), (d = (h = this.settings).closeCallback) == null || d.call(h), clearTimeout(s); }, 100); }).catch(() => { console.warn(`${g}: Font loading timeout, proceeding with print`); const s = setTimeout(() => { var a, c, h, d; o.focus(), (c = (a = this.settings).openCallback) == null || c.call(a), o.print(), t.remove(), (d = (h = this.settings).closeCallback) == null || d.call(h), clearTimeout(s); }, 100); }); }; (i = (r = this.settings).beforeOpenCallback) == null || i.call(r), this.addEvent(t, "load", n); } /** * 获取打印需要隐藏的 css * @private */ getNoPrintMediaStyle() { const { noPrintSelector: e } = this.settings; if (!e) return ""; const o = (Array.isArray(e) ? e : [e]).filter((n) => n.trim()); return o.length ? `${o.join(",")} { display: none; }` : (console.error(new TypeError(`${g}: The "noPrintSelector" must be a non-empty string or an array of strings.`)), ""); } write(e) { e.open(), e.write( `${this.docType()}<html lang='zh'>${this.getHead()}${this.getBody()}</html>` ), e.close(); } docType() { if (this.settings.standard === this.standards.html5) return "<!DOCTYPE html>"; const e = this.settings.standard === this.standards.loose ? " Transitional" : "", t = this.settings.standard === this.standards.loose ? "loose" : "strict"; return `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01${e}//EN" "http://www.w3.org/TR/html4/${t}.dtd">`; } /** * 获取媒体打印独有样式 * @private */ getPrintMediaStyle() { const { paperSize: e, orientation: t, customSize: o } = this.settings, n = "body {-webkit-print-color-adjust: exact; -moz-print-color-adjust: exact; -ms-print-color-adjust: exact; print-color-adjust: exact;}", r = this.settings.paperSize !== void 0 || this.settings.orientation !== void 0 || this.settings.customSize !== void 0; let i = ""; if (r) { const s = e || "A4", a = t || "portrait"; if (s === "custom" && o) { const { width: c, height: h, unit: d = "mm" } = o, p = b(c, d), f = b(h, d); i = a === "portrait" ? `@page { size: ${p} ${f}; }` : `@page { size: ${f} ${p}; }`; } else if (s in C) { const c = C[s]; i = a === "portrait" ? `@page { size: ${c.width} ${c.height}; }` : `@page { size: ${c.height} ${c.width}; }`; } else { const c = C.A4; i = t === "portrait" ? `@page { size: ${c.width} ${c.height}; }` : `@page { size: ${c.height} ${c.width}; }`; } } return `${n}${i}`; } getHead() { const { extraHead: e = "", extraCss: t = "", popTitle: o } = this.settings, n = Array.from(document.querySelectorAll('link[href$=".css"]')).map((h) => `<link type="text/css" rel="stylesheet" href='${h.href}'>`).join(""), r = Array.from(document.querySelectorAll("style")).map((h) => h.outerHTML).join(""), i = t.split(",").filter((h) => h.trim()).map((h) => `<link type="text/css" rel="stylesheet" href='${h.trim()}'>`).join(""), s = this.getPrintMediaStyle(), a = this.getNoPrintMediaStyle(); return `<head><title>${o}</title>` + e + n + r + `<style type="text/css">${a}${s}</style>` + i + "</head>"; } /** * 获取打印区的 DOM */ getPrintAreaDom() { const e = this.settings.el, t = typeof e == "string"; if (e instanceof HTMLElement) return [e]; if (!t) throw new TypeError(`${g}: The "el" property should be either a string (CSS selector) or an HTMLElement, but received type "${typeof e}".`); let o = Array.from(document.querySelectorAll(e)); if (!(o != null && o.length)) throw new Error(`${g}: No elements found matching the selector: "${e}".`); return o; } getBody() { const e = this.getPrintAreaDom(), t = document.createElement("div"); return e.forEach((o) => { const n = o.cloneNode(!0); this.removeScriptHandler(n), this.canvasToImgHandler(o, n), this.formDataHandler(o, n), t.appendChild(n); }), `<body>${t.innerHTML}</body>`; } /** * 移除 script 标签 * @param clonedElement */ removeScriptHandler(e) { const t = Array.from(e.querySelectorAll("script")); t.length !== 0 && t.forEach((o) => { o.remove(); }); } /** * 复制 Canvas 内容到新的 Canvas 元素 * @param originalElement * @param clonedElement * @private */ canvasToImgHandler(e, t) { try { const o = Array.from(e.querySelectorAll("canvas")), n = Array.from(t.querySelectorAll("canvas")); if (n.length === 0) return; let r = []; n.forEach((i, s) => { const a = o[s]; if (!a) { console.warn(`${g}: Canvas element mismatch, Canvas at index ${s} does not exist in the original element`); return; } const c = i.parentNode; if (!c) { console.warn(`${g}: Canvas parent element does not exist`); return; } try { const h = a.toDataURL("image/png"), d = new Image(); d.className = "canvasImg", d.style.display = "block", d.src = h, r.push({ parent: c, newElement: d, oldElement: i }); } catch (h) { console.error(`${g}: Error processing Canvas:`, h); const d = document.createElement("div"); d.className = "canvas-error", d.style.cssText = "padding: 10px; border: 1px dashed #ff6b6b; color: #ff6b6b; text-align: center;", d.textContent = "Unable to load Canvas content, possibly due to cross-origin restrictions", r.push({ parent: c, newElement: d, oldElement: i }); } }), r.forEach(({ parent: i, newElement: s, oldElement: a }) => { i.replaceChild(s, a); }); } catch (o) { console.error(`${g}: Error occurred while processing Canvas elements:`, o); } } /** * 根据type去处理form表单 * @param originalElement * @param clonedElement * @private */ formDataHandler(e, t) { try { const o = Array.from(t.querySelectorAll("input,select,textarea")); if (o.length === 0) return; const n = Array.from(e.querySelectorAll("select")); let r = -1; o.forEach((i) => { var s; try { const a = i.value; switch ((s = i.getAttribute("type")) != null ? s : i.tagName.toLowerCase()) { case "select": if (r++, r < n.length) { const d = n[r].selectedIndex, p = i.options; p && p.length > d && d >= 0 ? p[d].setAttribute("selected", "selected") : p && p.length > 0 ? (p[0].setAttribute("selected", "selected"), console.warn(`${g}: Select element option index out of range, defaulting to first option`)) : console.warn(`${g}: Select element has no options`); } else console.warn(`${g}: Original select element not found at index ${r}`); break; case "textarea": i.innerHTML = a, i.setAttribute("html", a); break; case "radio": case "checkbox": i.checked && i.setAttribute("checked", "checked"); break; default: i.value = a, i.setAttribute("value", a); break; } } catch (a) { console.error(`${g}: Error processing form element (${i.tagName}):`, a); } }); } catch (o) { console.error(`${g}: Error occurred while processing form data:`, o); } } // 生成并返回打印窗口的 iframe 和文档对 getPrintWindow(e) { var n; const t = this.createIframe(e), o = t.contentDocument || ((n = t.contentWindow) == null ? void 0 : n.document) || t.document; if (!o) throw new Error(`${g}: Unable to find the document object within the created iframe. Please ensure the iframe is correctly created and loaded.`); return { f: t, win: t.contentWindow || t, doc: o }; } // 显示预览窗口 previewBoxShow() { const e = document.getElementById("vue-print-next-previewBox"); if (!e) return; const t = document.querySelector("html"); t && (t.style.overflow = "hidden"), e.style.opacity = "0", e.style.display = "block", setTimeout(() => { e.style.transition = "opacity 0.3s ease", e.style.opacity = "1", this.applyInitialSettings(); }, 10); } // 应用初始设置 applyInitialSettings() { this.isDarkMode && (this.isDarkMode = !1, this.toggleTheme()), this.isFullscreen || (this.isFullscreen = !0, this.toggleFullscreen()), this.scale !== 1 && this.updatePreviewScale(); } // 隐藏预览窗口 previewBoxHide() { const e = document.getElementById("vue-print-next-previewBox"); e && (e.style.opacity = "0", setTimeout(() => { var n; const t = document.querySelector("html"); t && (t.style.overflow = "visible"); const o = e.querySelector(".paperContainer"); (n = o == null ? void 0 : o.querySelector("iframe")) == null || n.remove(), e.style.display = "none", this.resetPreviewState(e); }, 300)); } // 重置预览状态 resetPreviewState(e) { const t = e.querySelector(".previewBodyUtilPrintBtn"); t && (t.style.backgroundColor = "#1890ff", t.style.boxShadow = "none"), this.scale = this.settings.defaultScale || 1, this.isDarkMode && this.toggleTheme(), this.isFullscreen && (this.isFullscreen = !1, e.style.width = "100%", e.style.height = "100%", e.style.top = "0", e.style.left = "0", e.style.borderRadius = "0"); } // 创建或获取打印预览的框架 previewBox() { var o; let e = document.getElementById("vue-print-next-previewBox"); if (e) { (o = e.querySelector("iframe")) == null || o.remove(); const n = e.querySelector(".previewHeader"); if (n) { const s = this.createPreviewHeader(); e.replaceChild(s, n), this.close = s.querySelector(".previewClose"); } const r = e.querySelector(".paperContainer"); r && this.setPaperSize(r); const i = e.querySelector(".paperInfo"); return i && (i.innerHTML = w.paper + this.getPaperSizeText()), { close: this.close, previewBody: e.querySelector(".previewBody") }; } e = document.createElement("div"), e.setAttribute("id", "vue-print-next-previewBox"), e.setAttribute( "style", "position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background: rgba(250, 250, 250, 0.98); display: none; z-index: " + this.settings.zIndex + '; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); transition: background-color 0.3s ease;' ), e.setAttribute("data-theme", "light"); const t = this.createPreviewHeader(); return e.appendChild(t), this.previewBody = this.createPreviewBody(), e.appendChild(this.previewBody), document.body.appendChild(e), { close: this.close, previewBody: this.previewBody }; } // 创建预览框架的头部 createPreviewHeader() { const e = document.createElement("div"); e.setAttribute("class", "previewHeader"), e.setAttribute("style", "padding: 12px 24px; background: #fff; border-bottom: 1px solid #eaeaea; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); transition: background-color 0.3s ease, border-color 0.3s ease;"); const t = document.createElement("div"); t.setAttribute("style", "font-size: 16px; font-weight: 500; color: #333; transition: color 0.3s ease;"), t.innerHTML = this.settings.previewTitle || ""; const o = document.createElement("div"); o.setAttribute("style", "display: flex; align-items: center; gap: 12px;"); const n = { zoom: !0, theme: !0, fullscreen: !0 }; let r; if (this.settings.previewTools === !1) r = { zoom: !1, theme: !1, fullscreen: !1 }; else if (typeof this.settings.previewTools == "object" && this.settings.previewTools !== null) { const i = this.settings.previewTools; r = { zoom: i.zoom !== !1, theme: i.theme !== !1, fullscreen: i.fullscreen !== !1 }; } else r = E({}, n); if (r.zoom) { const i = this.createZoomControls(); o.appendChild(i); } if (r.theme) { const i = this.createThemeToggle(); o.appendChild(i); } if (r.fullscreen) { const i = this.createFullscreenToggle(); o.appendChild(i); } return this.close = this.createCloseButton(), e.appendChild(t), e.appendChild(o), e.appendChild(this.close), e; } // 创建iframe元素 createIframe(e) { const t = document.createElement("iframe"); if (t.id = this.iframeId, t.src = e || "about:blank", t.style.cssText = "position: fixed; left: -9999px; border: 0; width: 0; height: 0; opacity: 0;", !this.settings.preview) document.body.appendChild(t); else { t.style.cssText = "border: 0px; width: 100%; height: 100%; background: white; transition: background-color 0.3s ease;"; const { close: o, previewBody: n } = this.previewBox(), r = n == null ? void 0 : n.querySelector(".paperContainer"), i = r == null ? void 0 : r.querySelector("div"); i == null || i.appendChild(t), this.updatePreviewScale(), this.addEvent(o, "click", this.previewBoxHide.bind(this)), this.addEvent(t, "load", this.applyIframeDarkMode.bind(this, t)); } return t; } // 为iframe应用深色模式 applyIframeDarkMode(e) { var t; if (this.isDarkMode) try { const o = e.contentDocument || ((t = e.contentWindow) == null ? void 0 : t.document); if (o) { const n = o.createElement("style"); n.textContent = "body { color-scheme: dark; }", o.head.appendChild(n); } } catch (o) { console.warn("[VuePrintNext] Failed to apply dark mode style to iframe content", o); } } // 创建关闭按钮 createCloseButton() { const e = document.createElement("div"); return e.setAttribute("class", "previewClose"), e.setAttribute("style", "width: 32px; height: 32px; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: background-color 0.2s ease, color 0.3s ease;"), e.innerHTML = w.close, x(e, this.isDarkMode), e; } // 创建缩放控制 createZoomControls() { const e = document.createElement("div"); e.setAttribute("class", "zoomControls"), e.setAttribute("style", "display: flex; align-items: center; gap: 8px;"); const t = B("zoomOutBtn", w.zoomOut, 28); x(t, this.isDarkMode), t.addEventListener("click", () => { this.scale = Math.max(0.5, this.scale - 0.1), this.updatePreviewScale(); }); const o = document.createElement("span"); o.setAttribute("class", "zoomDisplay"), o.setAttribute("style", "font-size: 14px; min-width: 40px; text-align: center;"), o.textContent = "100%"; const n = B("zoomInBtn", w.zoomIn, 28); x(n, this.isDarkMode), n.addEventListener("click", () => { this.scale = Math.min(2, this.scale + 0.1), this.updatePreviewScale(); }); const r = document.createElement("button"); return r.setAttribute("class", "zoomResetBtn"), r.setAttribute("style", "font-size: 12px; padding: 2px 6px; border: none; background: transparent; cursor: pointer; border-radius: 4px; transition: background-color 0.2s ease;"), r.textContent = "重置", x(r, this.isDarkMode), r.addEventListener("click", () => { this.scale = this.settings.defaultScale || 1, this.updatePreviewScale(); }), e.appendChild(t), e.appendChild(o), e.appendChild(n), e.appendChild(r), e; } // 更新预览缩放比例 updatePreviewScale() { const e = document.getElementById("vue-print-next-previewBox"); if (!e) return; const t = e.querySelector(".zoomDisplay"); t && (t.textContent = `${Math.round(this.scale * 100)}%`); const o = e.querySelector(".paperContainer"); o && (o.style.transform = `scale(${this.scale})`, o.style.transformOrigin = "center top"); } // 创建主题切换按钮 createThemeToggle() { const e = B("themeToggle", w.darkMode, 32, "切换深色/浅色模式"); return x(e, this.isDarkMode), e.addEventListener("click", () => { this.toggleTheme(); }), e; } // 切换深色/浅色主题 toggleTheme() { var v, m, M, P, $; this.isDarkMode = !this.isDarkMode; const e = document.getElementById("vue-print-next-previewBox"); if (!e) return; const t = this.isDarkMode ? { theme: "dark", background: "rgba(30, 30, 30, 0.98)", headerBg: "#242424", borderColor: "#333", textColor: "#fff", contentBg: "#1a1a1a", pageInfoColor: "#ccc", themeIcon: w.lightMode } : { theme: "light", background: "rgba(250, 250, 250, 0.98)", headerBg: "#fff", borderColor: "#eaeaea", textColor: "#333", contentBg: "#f5f5f5", pageInfoColor: "#666", themeIcon: w.darkMode }; e.setAttribute("data-theme", t.theme), e.style.backgroundColor = t.background; const o = e.querySelector(".previewHeader"); o && (o.style.backgroundColor = t.headerBg, o.style.borderColor = t.borderColor); const n = e.querySelector(".previewHeader > div:first-child"); n && (n.style.color = t.textColor), e.querySelectorAll(".previewHeader button, .previewHeader svg, .zoomDisplay, .zoomResetBtn").forEach((u) => { u.style.color = t.textColor; }); const i = e.querySelector(".previewBodyUtil"); i && (i.style.backgroundColor = t.headerBg, i.style.borderColor = t.borderColor); const s = e.querySelector(".previewBody > div:last-child"); s && (s.style.backgroundColor = t.contentBg); const a = e.querySelector(".pageInfo"); a && (a.style.color = t.pageInfoColor); const c = e.querySelector(".themeToggle"); if (c) { const u = c.cloneNode(!0); u.innerHTML = t.themeIcon, (v = c.parentNode) == null || v.replaceChild(u, c), x(u, this.isDarkMode), u.addEventListener("click", () => { this.toggleTheme(); }); } const h = e.querySelector(".zoomInBtn"); if (h) { const u = h.cloneNode(!0); (m = h.parentNode) == null || m.replaceChild(u, h), x(u, this.isDarkMode), u.addEventListener("click", () => { this.scale = Math.min(2, this.scale + 0.1), this.updatePreviewScale(); }); } const d = e.querySelector(".zoomOutBtn"); if (d) { const u = d.cloneNode(!0); (M = d.parentNode) == null || M.replaceChild(u, d), x(u, this.isDarkMode), u.addEventListener("click", () => { this.scale = Math.max(0.5, this.scale - 0.1), this.updatePreviewScale(); }); } const p = e.querySelector(".zoomResetBtn"); if (p) { const u = p.cloneNode(!0); (P = p.parentNode) == null || P.replaceChild(u, p), x(u, this.isDarkMode), u.addEventListener("click", () => { this.scale = this.settings.defaultScale || 1, this.updatePreviewScale(); }); } const f = e.querySelector(".fullscreenToggle"); if (f) { const u = f.cloneNode(!0); u.innerHTML = this.isFullscreen ? w.fullscreen : w.exitFullscreen, ($ = f.parentNode) == null || $.replaceChild(u, f), x(u, this.isDarkMode), u.addEventListener("click", () => { this.toggleFullscreen(); }); } const y = e.querySelector(".previewBodyUtilPrintBtn"); y && (y.addEventListener("mouseover", () => { y.style.backgroundColor = "#40a9ff", y.style.boxShadow = "0 2px 8px rgba(24, 144, 255, 0.3)"; }), y.addEventListener("mouseout", () => { y.style.backgroundColor = "#1890ff", y.style.boxShadow = "none"; })); } // 创建全屏切换按钮 createFullscreenToggle() { const e = B("fullscreenToggle", w.fullscreen, 32, "切换全屏/弹窗模式"); return x(e, this.isDarkMode), e.addEventListener("click", () => { this.toggleFullscreen(); }), e; } // 切换全屏/弹窗模式 toggleFullscreen() { this.isFullscreen = !this.isFullscreen; const e = document.getElementById("vue-print-next-previewBox"); if (!e) return; const t = this.isFullscreen ? { width: "100%", height: "100%", top: "0", left: "0", borderRadius: "0", boxShadow: "0 0 20px rgba(0, 0, 0, 0.1)", icon: w.fullscreen } : { width: "80%", height: "80%", top: "10%", left: "10%", borderRadius: "8px", boxShadow: "0 0 30px rgba(0, 0, 0, 0.2)", icon: w.exitFullscreen }; e.style.width = t.width, e.style.height = t.height, e.style.top = t.top, e.style.left = t.left, e.style.borderRadius = t.borderRadius, e.style.boxShadow = t.boxShadow; const o = e.querySelector(".fullscreenToggle"); o && (o.innerHTML = t.icon); } // 获取纸张信息文本 getPaperSizeText() { const { paperSize: e = "A4", orientation: t = "portrait", customSize: o, previewSize: n } = this.settings; let r = ""; if (n) { if (typeof n == "string") r = n; else if (n.width && n.height) { const { width: i, height: s, unit: a = "mm" } = n, c = b(i, a), h = b(s, a); r = `自定义 (${c} × ${h})`; } } else if (e === "custom" && o) { const { width: i, height: s, unit: a = "mm" } = o, c = b(i, a), h = b(s, a); r = `自定义 (${c} × ${h})`; } else r = e; return `${r} - ${t === "portrait" ? "纵向" : "横向"}`; } // 创建预览主体 createPreviewBody() { const e = document.createElement("div"); e.className = "previewBody", e.style.cssText = "display: flex; flex-direction: column; height: calc(100% - 56px);"; const t = document.createElement("div"); t.className = "previewBodyUtil", t.style.cssText = "height: 48px; background: #fff; position: relative; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; border-bottom: 1px solid #eaeaea; transition: background-color 0.3s ease, border-color 0.3s ease;"; const o = document.createElement("div"); o.className = "leftTools", o.style.cssText = "display: flex; align-items: center; gap: 16px;"; const n = document.createElement("button"); n.className = "previewBodyUtilPrintBtn", n.style.cssText = "padding: 8px 16px; font-size: 14px; color: white; cursor: pointer; background: #1890ff; border: none; border-radius: 4px; transition: all 0.3s ease; outline: none; font-weight: 500; display: flex; align-items: center; justify-content: center;", n.innerHTML = w.print + (this.settings.previewPrintBtnLabel || ""), n.addEventListener("mouseover", () => { n.style.backgroundColor = "#40a9ff", n.style.boxShadow = "0 2px 8px rgba(24, 144, 255, 0.3)"; }), n.addEventListener("mouseout", () => { n.style.backgroundColor = "#1890ff", n.style.boxShadow = "none"; }), o.appendChild(n); const r = document.createElement("div"); r.className = "paperInfo", r.style.cssText = "font-size: 13px; color: #666; display: flex; align-items: center; transition: color 0.3s ease;", r.innerHTML = w.paper + this.getPaperSizeText(), o.appendChild(r), t.appendChild(o), e.appendChild(t); const i = document.createElement("div"); i.className = "contentContainer", i.style.cssText = "flex: 1; padding: 24px; overflow: auto; background: #f5f5f5; display: flex; justify-content: center; transition: background-color 0.3s ease;"; const s = document.createElement("div"); s.className = "paperContainer", s.style.cssText = "background: white; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); transition: box-shadow 0.3s ease; transform-origin: center top; margin-bottom: 24px;", this.setPaperSize(s); const a = document.createElement("div"); return a.style.cssText = "width: 100%; height: 100%; overflow: hidden;", s.appendChild(a), i.appendChild(s), e.appendChild(i), e; } // 获取纸张尺寸 getPaperDimensions() { const { paperSize: e = "A4", customSize: t, previewSize: o } = this.settings, n = C.A4; let r = n.width, i = n.height; if (o) { if (typeof o == "string") { if (o in C) { const s = C[o]; r = s.width, i = s.height; } } else if (o.width && o.height) { const { width: s, height: a, unit: c = "mm" } = o; r = b(s, c), i = b(a, c); } } else if (e === "custom" && t) { const { width: s, height: a, unit: c = "mm" } = t; r = b(s, c), i = b(a, c); } else if (e in C) { const s = C[e]; r = s.width, i = s.height; } return { width: r, height: i }; } // 设置纸张尺寸和方向 setPaperSize(e) { const { orientation: t = "portrait" } = this.settings, { width: o, height: n } = this.getPaperDimensions(); t === "portrait" ? (e.style.width = o, e.style.height = n) : (e.style.width = n, e.style.height = o); } /** * 等待字体加载完成 * @param targetWindow 目标窗口(iframe的contentWindow或当前窗口) * @returns Promise<void> */ waitForFontsLoaded(e) { return new Promise((t, o) => { const r = Date.now(); if (e.document.fonts && typeof e.document.fonts.ready == "object") Promise.race([ e.document.fonts.ready, new Promise((i, s) => { setTimeout(() => s(new Error("Font loading timeout")), 3e3); }) ]).then(() => { t(); }).catch(() => { o(new Error("Font loading timeout or failed")); }); else { const i = () => { if (Date.now() - r >= 3e3) { o(new Error("Font loading timeout")); return; } e.document.readyState === "complete" ? setTimeout(() => t(), 200) : setTimeout(i, 50); }; i(); } }); } } const R = (l, e, t) => { l.addEventListener ? l.addEventListener(e, t, !1) : l.attachEvent ? l.attachEvent("on" + e, t) : l["on" + e] = t; }, A = { directiveName: "print", // vue3 指定挂载 mounted(l, e) { let t, o = {}; R(l, "click", () => { e.value ? typeof e.value == "string" ? t = e.value : typeof e.value == "object" && (t = e.value.el, o = e.value) : t = "body", new W(z(E({}, o), { el: t, vue: e.instance })); }); }, // 兼容 Vue2 指令挂载 bind(l, e, t) { e.instance = t.context, A.mounted(l, e); } }, Z = { install(l) { l.directive(A.directiveName, A); } }; export { W as VuePrintNext, Z as printPlugin, A as vPrint };