import { ref, reactive } from 'vue' export const usePdfPreview = () => { const state = reactive({ visible: false, url: '', name: '', blobUrl: '', images: [] as string[] }) const frameRef = ref(null) // 动态加载 pdf.js(优先本地依赖,失败回退 CDN) const loadPdfJs = async (): Promise => { // 统一使用 CDN,避免本地依赖缺失导致打包时报错 const ensure = (src: string) => new Promise((resolve, reject) => { const s = document.createElement('script') s.src = src s.onload = () => resolve() s.onerror = () => reject(new Error('load script failed')) document.head.appendChild(s) }) await ensure('https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js') await ensure('https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js') return (window as any).pdfjsLib } const open = (url: string, name = '') => { if (!url) return state.url = url state.name = name state.visible = true state.images = [] } const openWithResponse = (res: any) => { const url = res?.data?.file_url_full || res?.file_url_full const name = res?.data?.file_url || res?.file_url || '' open(url, name) } const inferFilenameFromUrl = (url: string): string => { try { const u = new URL(url) const parts = u.pathname.split('/') return parts[parts.length - 1] || 'document.pdf' } catch (e) { const parts = (url || '').split('?')[0].split('/') return parts[parts.length - 1] || 'document.pdf' } } const download = async () => { if (!state.url) return const filename = state.name || inferFilenameFromUrl(state.url) try { const resp = await fetch(state.url, { credentials: 'omit' }) const blob = await resp.blob() const url = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = filename document.body.appendChild(link) link.click() document.body.removeChild(link) window.URL.revokeObjectURL(url) } catch { window.open(state.url, '_blank') } } // 预览时也用 blob,提升兼容性 const preparePreview = async () => { if (!state.url) return try { const resp = await fetch(state.url, { credentials: 'omit' }) const blob = await resp.blob() state.blobUrl && window.URL.revokeObjectURL(state.blobUrl) state.blobUrl = window.URL.createObjectURL(blob) } catch { state.blobUrl = '' } } // 生成图片以便无需浏览器 PDF 插件即可展示 const prepareImages = async () => { if (!state.url) return state.images = [] try { const resp = await fetch(state.url, { credentials: 'omit' }) const arrayBuffer = await resp.arrayBuffer() const pdfjsLib = await loadPdfJs() const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise const images: string[] = [] for (let p = 1; p <= pdf.numPages; p++) { const page = await pdf.getPage(p) const viewport = page.getViewport({ scale: 1.5 }) const canvas = document.createElement('canvas') const context = canvas.getContext('2d') if (!context) continue canvas.width = viewport.width canvas.height = viewport.height await page.render({ canvasContext: context, viewport }).promise images.push(canvas.toDataURL('image/png')) } state.images = images } catch { state.images = [] } } const print = () => { const frame = frameRef.value if (frame && frame.contentWindow) { frame.contentWindow.focus() frame.contentWindow.print() return } if (state.url) { const win = window.open(state.url, '_blank') if (win) { win.addEventListener('load', () => win.print()) } } } return { pdf: state, frameRef, open, openWithResponse, download, print, preparePreview, prepareImages } }