141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
|
|
import { ref, reactive } from 'vue'
|
||
|
|
|
||
|
|
export const usePdfPreview = () => {
|
||
|
|
const state = reactive({
|
||
|
|
visible: false,
|
||
|
|
url: '',
|
||
|
|
name: '',
|
||
|
|
blobUrl: '',
|
||
|
|
images: [] as string[]
|
||
|
|
})
|
||
|
|
const frameRef = ref<HTMLIFrameElement | null>(null)
|
||
|
|
|
||
|
|
// 动态加载 pdf.js(优先本地依赖,失败回退 CDN)
|
||
|
|
const loadPdfJs = async (): Promise<any> => {
|
||
|
|
// 统一使用 CDN,避免本地依赖缺失导致打包时报错
|
||
|
|
const ensure = (src: string) =>
|
||
|
|
new Promise<void>((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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|