import { getArrValue, getRandom, isNullES, isNumber, isString } from 'js-fast-way' //PDF签章 export default class HcPdfSign { static iframeDom = null static pdfViewer = null static signImgCss = {} static signType = '' static disX = 0 static disY = 0 static curSignDom = null static signList = [] static signChange = null static pdfLoadFunc = null static batchSign = false static signNum = 0 /** * 初始化创建PDF预览 * @param ele 元素的ref,或id值 * @param url pdf的url * @param img 签章图片url * @param func 回调函数 */ static createPdf({ ele, url, img, change, load }) { this.initVarNull() //判断参数是否合法 if (isNullES(ele)) { console.warn('请传入参数,必须是,ref的dom元素,或id值') return false } //是否为字符串或数值类型 let dom = null, errTip = 'ele参数不合法,必须是ref的dom元素,或id值' if (isString(ele) || isNumber(ele)) { try { dom = document.getElementById(ele) } catch (e) { console.error(errTip) return false } } else { try { ele.cloneNode(true) if (ele.nodeType === 1 || ele.nodeType === 9) { dom = ele } else { console.error(errTip) return false } } catch (e) { console.error(errTip) return false } } //清空dom const children = dom.children if (children.length > 0) { for (let i = children.length - 1; i >= 0; i--) { dom.removeChild(children[i]) } } if (isNullES(url)) { console.warn('请传入Pdf的url参数') return false } //创建iframe const iframe = document.createElement('iframe') iframe.setAttribute('id', 'pdf-sign-' + getRandom(6)) iframe.src = `/plugins/pdfjs/web/viewer.html?file=${url}#zoom=100` iframe.style.width = '100%' iframe.style.height = '100%' iframe.style.border = '1px solid #ccc' iframe.name = 'HcPdfSign' iframe.addEventListener('load', () => { this.iframeLoad() }) dom.appendChild(iframe) this.iframeDom = iframe //设置签章图片 if (isNullES(img)) { console.warn('请传入签章图片') return false } this.signImage(img) //设置回调事件 if (!isNullES(change)) { this.addEventFunc(change) } //设置回调事件 if (!isNullES(load)) { this.addPdfLoadFunc(load) } } //初始化空值 static initVarNull() { this.iframeDom = null this.pdfViewer = null this.signImgCss = {} this.signType = '' this.disX = 0 this.disY = 0 this.curSignDom = null this.signList = [] this.signChange = null this.pdfLoadFunc = null this.batchSign = false this.signNum = 0 } //设置回调事件 static addEventFunc(func) { if (typeof func !== 'function') { console.warn('请传入函数') return false } else { this.signChange = func } } //触发回调事件 static signChangeFunc() { if (typeof this.signChange !== 'function') { return false } else { this.signChange(this.signList) } } //设置加载完成的回调事件 static addPdfLoadFunc(func) { if (typeof func !== 'function') { console.warn('请传入函数') return false } else { this.pdfLoadFunc = func } } //加载完成 static setPdfLoadFunc(val) { if (typeof this.pdfLoadFunc !== 'function') { return false } else { this.pdfLoadFunc(val) } } //设置签章图片 static signImage(url) { this.signImgCss = { url: url, width: 100, height: 26 } } //创建签章图片 static async createSignImage(event) { //创建图片元素 const { url, width, height } = this.signImgCss const uuid = 'pdf-sign-img-' + getRandom(6) const signImg = document.createElement('img') signImg.setAttribute('id', uuid) signImg.src = url signImg.style.position = 'absolute' signImg.style.width = width + 'px' signImg.style.height = height + 'px' signImg.style.left = (event.layerX - (width / 2)) + 'px' signImg.style.top = (event.layerY - (height / 2)) + 'px' signImg.style.pointerEvents = 'auto' signImg.style.zIndex = 999 return signImg } /** * 是否批量签章 * @param val true/false */ static setBatchSign(val = false) { this.batchSign = val } //让PDF页面全部渲染一下 static async setPageScrolling() { const pageDom = this.pdfViewer?.children ?? [] await this.toPdfPage('firstPage') for (let i = 0; i < pageDom.length; i++) { await this.toPdfPage('next') } await this.toPdfPage('firstPage') } /** * 跳转pdf的页面 * @param pageId 最后一页:lastPage,第一页:firstPage,上一页:previous,下一页:next */ static async toPdfPage(pageId) { return new Promise((resolve) => { this.iframeDom?.contentDocument?.getElementById(pageId)?.click() setTimeout(() => { resolve(true) }, 100) }) } //PDF加载完成 static iframeLoad() { const viewer = this.iframeDom?.contentDocument?.getElementById('viewer') //页面被点击 viewer.addEventListener('click', (event) => { this.viewerClick(event).then() }) //鼠标移动 viewer.addEventListener('mousemove', (event) => { event.preventDefault() if (this.signType !== '移动' || !this.curSignDom) { return } //鼠标位置 const left = (event.clientX - this.disX) + 'px' const top = (event.clientY - this.disY) + 'px' this.curSignDom.style.left = left this.curSignDom.style.top = top //批量处理 if (this.batchSign) { const curDom = this.curSignDom let { data: curSign } = this.getSignImgDom(curDom) if (!curSign) return const newArr = this.signList.filter(item => { return item.type === curSign.type }) newArr.forEach(item => { item.dom.style.left = left item.dom.style.top = top }) } }) //鼠标抬起 viewer.addEventListener('mouseup', () => { if (!this.curSignDom) return this.signType = '签名' this.curSignDom.style.cursor = 'default' const curDom = this.curSignDom //获取数据 let { data: curSign } = this.getSignImgDom(curDom) if (!curSign) return //计算坐标 const left = curDom.style.left.replace('px', '') const top = curDom.style.top.replace('px', '') const { width, height } = this.signImgCss const lefts = Number(left) + Number(width / 2) const tops = Number(top) + Number(height / 2) const lx = ((lefts * 100) / curDom.parentNode.clientWidth).toFixed(4) const ly = ((tops * 100) / curDom.parentNode.clientHeight).toFixed(4) //批量处理 if (this.batchSign) { const newArr = this.signList.filter(item => { return item.type === curSign.type }) newArr.forEach(item => { item.lx = lx item.ly = ly }) } else { curSign.lx = lx curSign.ly = ly } this.signChangeFunc() }) //监听按键 const container = this.iframeDom?.contentDocument?.getElementById('outerContainer') container.addEventListener('keyup', (event) => { //删除签章 if (event.keyCode === 8 && event.key === 'Backspace') { const curDom = this.curSignDom const { index } = this.getSignImgDom(curDom) if (index <= -1) return this.delSignImg(index).then() } }) this.pdfViewer = viewer //处理pdf渲染 if (this.batchSign) { setTimeout(async () => { await this.setPageScrolling() this.setPdfLoadFunc(true) }, 500) } else { setTimeout(async () => { this.setPdfLoadFunc(true) }, 100) } } //删除签章 static async delSignImg(index) { if (index <= -1) return if (this.batchSign) { const list = this.signList, curDom = list[index] //倒序删除签章 for (let i = list.length - 1; i >= 0; i--) { if (list[i].type === curDom.type) { list[i].dom.remove() delete list[i].dom list.splice(i, 1) } } this.signList = list this.signChangeFunc() } else { this.signList[index].dom.remove() this.signList.splice(index, 1) this.signChangeFunc() } } //获取签章图片的数据 static getSignImgDom(curDom) { if (isNullES(curDom)) { return { data: null, index: -1 } } //获取数据 const id = curDom?.getAttribute('id') let curSign = null, index = -1 for (let i = 0; i < this.signList.length; i++) { const item = this.signList[i] if (item.id === id) { curSign = item index = i break } } return { data: curSign, index } } //页面被点击 static async viewerClick(event) { const target = event.target //当前为移动模式 if (this.signType === '移动') { return } //不在PDF页面上点击 if (target.className !== 'textLayer') { return } //批量签章 this.signNum ++ if (this.batchSign) { const parent = target?.parentNode?.parentNode?.children ?? [] for (let i = 0; i < parent.length; i++) { await this.setPdfNodeSign(parent[i], parent[i]?.children[1], event) } this.signChangeFunc() } else { await this.setPdfNodeSign(target?.parentNode, target, event) this.signChangeFunc() } } //设置PDF节点签章 static async setPdfNodeSign(node, textLayer, event) { if (isNullES(node) || isNullES(textLayer) || isNullES(event)) { return } if (node.children.length < 3) { node.prepend('
') } //插入图片 const signImgDom = await this.createSignImage(event) node.children[0].append(signImgDom) //鼠标按下 signImgDom.addEventListener('mousedown', (e) => { //新的当前选中项 this.curSignDom = e.target this.signType = '移动' e.target.style.cursor = 'move' //鼠标相对于图片的位置 this.disX = e.clientX - e.target.offsetLeft this.disY = e.clientY - e.target.offsetTop }) //保存签章信息 this.signList.push({ type: this.signNum, url: this.signImgCss?.url ?? '', page: node.getAttribute('data-page-number'), id: signImgDom.getAttribute('id'), lx: ((event.layerX * 100) / textLayer.clientWidth).toFixed(4), ly: ((event.layerY * 100) / textLayer.clientHeight).toFixed(4), dom: signImgDom, }) return signImgDom } //设置PDF签章图片 static async setPdfSignImg(arr) { const list = getArrValue(arr) if (list.length <= 0) return const pageDom = this.pdfViewer?.children ?? [] for (let i = 0; i < list.length; i++) { const page = Number(list[i].page) - 1 const dom = pageDom[page].getElementsByClassName('canvasWrapper')[0] //创建图片元素 const signImg = document.createElement('img') signImg.setAttribute('id', list[i].id) signImg.src = list[i].url signImg.style.position = 'absolute' signImg.style.width = list[i].dom.style.width signImg.style.height = list[i].dom.style.height signImg.style.left = list[i].dom.style.left signImg.style.top = list[i].dom.style.top signImg.style.pointerEvents = 'auto' signImg.style.zIndex = 999 dom.append(signImg) //鼠标按下 signImg.addEventListener('mousedown', (e) => { //新的当前选中项 this.curSignDom = e.target this.signType = '移动' e.target.style.cursor = 'move' //鼠标相对于图片的位置 this.disX = e.clientX - e.target.offsetLeft this.disY = e.clientY - e.target.offsetTop }) //保存签章信息 this.signList.push({ ...list[i], dom: signImg, }) } } }