vue 开发拖拽签章功能。 主要应用,pdf 合同 盖章, 文件水印, 图文打码
实现原理)通过拖拽功能实现 (主要vue代码,通过后台将pdf转换 统一格式的图片,前端分页 再不同页面添加签章。)签章 使用draggable="true" @dragstart="drag($event)"签章容器 使用@drop="drop($event, docindex+1)" @dragover="allowDrop($event)"本源码实现是pdf...
·
实现原理)
通过拖拽功能实现 (主要vue代码,通过后台将pdf转换 统一格式的图片,前端分页 再不同页面添加签章。)
签章 使用 draggable="true" @dragstart="drag($event)"
签章容器 使用
@drop="drop($event, docindex+1)" @dragover="allowDrop($event)"
本源码实现是pdf 分页签章功能, 计算单位是磅。
pt_y: 842, // pdf 的磅宽
pt_x: 595, // pdf 的磅高 此为基础
优势特点
签章不会出现夸页面问题。
响应式开发,适用于不同屏幕尺寸 (pc 端为主。移动端设备效果不佳)
代码如下 (附带了滚动分页功能)
<template>
<Row class="treaty">
<i-col span="3" class="pageList">
<p class="pageTitle">
页码切换
</p>
<div ref="pagePanel" class="pagePanel">
<div v-for="(item, index) in imgList" :key="index" class="item" :class="{on: index == selectPage }" @click="selectItem(index)">
<img :src="`data:image/png;base64,${item.url}`">
<p>
{{ index+1 }}
</p>
</div>
</div>
</i-col>
<i-col span="17">
<div ref="docContainer" class="imgListpanel">
<div class="imgpanel">
<div v-for="(docItem, docindex) in imgList" :key="docindex" ref="docItem" class="imgList" @drop="drop($event, docindex+1)" @dragover="allowDrop($event)">
<img :src="`data:image/png;base64,${docItem.url}`">
</div>
</div>
</div>
</i-col>
<i-col span="4">
<div class="rightPanel">
<div class="item">
<p class="title">
发起人
</p>
<p>
{{ initiatorLoginName }}
<span class="on">
{{ contractStatus(contractDetail.status) }}
</span>
</p>
</div>
<active v-if="contractDetail.contractType === '01'">
<div class="item">
<p class="title">
签署人
</p>
<div v-for="(singItem, index) in signPersonList" :key="index">
<p>
{{ singItem.participantLoginName }}
<span :class="{on: singItem.operationResult === '01' }">
{{ singItem.operationResult ? operationResultStatus[singItem.operationResult] : '未签署' }}
</span>
</p>
<p>
{{ signPersonList[index].instName }}
</p>
<p class="line" />
</div>
</div>
<div v-if="optype === 'sign'" class="item">
<p class="title">
签章
</p>
<div v-if="signStr">
<p class="fic">
拖拽签章到合同中
</p>
<div class="dropDom" draggable="true" @dragstart="drag($event)">
<img :src="`data:image/png;base64,${signStr}`">
<span>
<Icon type="close-circled" style="cursor: pointer;" />
</span>
</div>
</div>
<div v-else style="text-align: center;">
<p class="fic">
暂无签章,请先上传
</p>
<Button type="ghost" @click="toSignatureManege">
立即上传
</Button>
</div>
</div>
</active>
</div>
</i-col>
</Row>
</template>
<script>
import tool from '@/utils/tool'
let dom = null
export default {
name: 'TreatyContentPreview',
directives: {
drag (el) {
el.onmousedown = (e) => {
let disx = e.pageX - el.offsetLeft
let disy = e.pageY - el.offsetTop
e.preventDefault()
document.onmousemove = (e) => {
el.style.left = e.pageX - disx + 'px'
el.style.top = e.pageY - disy + 'px'
e.preventDefault()
}
document.onmouseup = () => {
document.onmousemove = document.onmouseup = null
}
}
}
},
props: {
imgList: {
type: Array,
default: () => { return [] }
},
optype: {
type: String,
default: ''
},
initiatorLoginName: {
type: String,
default: ''
},
signStr: {
type: String,
default: ''
},
contractDetail: {
type: Array,
default: () => { return {} }
},
signPersonList: {
type: Array,
default: () => { return [] }
}
},
data () {
return {
selectPage: 0,
mouseInSealX: 0,
mouseInSealY: 0,
operationResultStatus: {
'01': '已签署',
'02': '拒签',
'03': '已撤销'
},
sealWidth: 0,
sealHeight: 0,
pt_y: 842, // pdf 的磅宽
pt_x: 595, // pdf 的磅高
signaturelocation: null
}
},
computed: {
docWidth () {
return this.$refs.docItem[0].offsetWidth
},
docHeight () {
return this.$refs.docItem[0].offsetHeight
},
naturalWidth () {
return this.$refs.docItem[0].children[0].naturalWidth
},
naturalHeight () {
return this.$refs.docItem[0].children[0].naturalHeight
}
},
watch: {
selectPage (oldVal, newVal) {
console.log(oldVal + 'qss' + newVal)
}
},
activated () {
this.sealWidth = 0
dom = null
// 监听这个dom的scroll事件
this.$refs.docContainer.addEventListener('scroll', () => {
let step = this.$refs.docContainer.scrollHeight / this.imgList.length
let scrollTop = this.$refs.docContainer.scrollTop
if (Math.round(scrollTop / step) < this.imgList.length) {
this.selectPage = Math.round(scrollTop / step)
}
let pageStep = this.$refs.pagePanel.scrollHeight / this.imgList.length
this.$refs.pagePanel.scrollTop = pageStep * this.selectPage
}, false)
},
beforeDestroy () {
this.sealWidth = 0
dom = null
},
methods: {
contractStatus (status) {
let statusText = {
'01': '草稿',
'02': '用印中',
'03': '签约完成',
'04': '拒签',
'05': '已撤销',
'06': '已过期',
'07': '文件归档',
'08': '文件作废'
}
let returnList = ['02', '05', '07', '08']
if (returnList.indexOf(status) !== -1) {
// 电子签约
if (status === '05') {
return '已撤销'
}
if (this.contractDetail.contractType === '01') {
return ''
}
return statusText[status]
} else {
return ''
}
},
removeElement (_element) {
let _parentElement = _element.parentNode
if (_parentElement) {
_parentElement.removeChild(_element)
}
},
toSignatureManege () {
this.$router.push({ path: '/account/signatureManege', query: { backPath: '/contract/list', menuName: 'SignatureManege' } })
},
iconClick () {
if (dom) {
// dom.remove()
this.removeElement(dom)
dom = null
this.signaturelocation = null
}
},
drag (event) {
if (dom) {
// dom.remove()
this.removeElement(dom)
dom = null
this.signaturelocation = null
}
let scale = this.naturalWidth / this.docHeight
dom = event.currentTarget.cloneNode(true)
this.mouseInSealX = event.offsetX
this.mouseInSealY = event.offsetY
this.sealWidth = event.currentTarget.offsetWidth / scale
this.sealHeight = event.currentTarget.offsetHeight / scale
// 30 和 50 是 外框边距。用于删除
this.sealWidth = dom.children[0].naturalWidth / scale + 50
this.sealHeight = dom.children[0].naturalHeight / scale + 30
},
drop (event, index) {
if (this.optype !== 'sign') {
return false
}
event.preventDefault()
let target = event.currentTarget
// let docWidth = this.$refs.docItem.offsetWidth
// docOffsetLeft 文档到docment的offsetLeft
let docOffsetLeft = tool.getPos(target).left
let docOffsetTop = tool.getPos(target).top
let scrollTop = this.$refs.docContainer.scrollTop
// inDocLeft 签章在doc中的left
let inDocLeft = event.clientX - docOffsetLeft - this.mouseInSealX
// inDocTop 签章在doc中的top
let inDocTop = event.clientY - (docOffsetTop - scrollTop) - this.mouseInSealY
dom.style.position = 'absolute'
dom.style.left = inDocLeft + 'px'
dom.style.top = inDocTop + 'px'
dom.children[1].style.display = 'block'
dom.style.width = this.sealWidth + 'px'
dom.style.height = this.sealHeight + 'px'
// 以当前页面左下角位原点 ,取 左下角 (offsetX,offsetY) 右上角 (locationX,locationY)
let offsetX = inDocLeft
let offsetY = this.docHeight - (inDocTop + this.sealHeight)
console.log('(' + offsetX + ',' + offsetY + ')')
// 30 和 50 是 外框边距
this.signaturelocation = {
locationX: ((offsetX + this.sealWidth - 25) / this.docWidth * this.pt_x).toFixed(0),
locationY: ((offsetY + this.sealHeight - 15) / this.docHeight * this.pt_y).toFixed(0),
offsetX: ((offsetX + 25) / this.docWidth * this.pt_x).toFixed(0),
offsetY: ((offsetY + 15) / this.docHeight * this.pt_y).toFixed(0),
signMode: '1',
signOnPage: index.toString()
}
console.log(this.signaturelocation)
let isAdd = (this.signaturelocation.locationX <= 0 || this.signaturelocation.locationX >= this.pt_x ||
this.signaturelocation.locationY <= 0 || this.signaturelocation.locationY >= this.pt_y ||
this.signaturelocation.offsetX <= 0 || this.signaturelocation.offsetX >= this.pt_x ||
this.signaturelocation.offsetY <= 0 || this.signaturelocation.offsetY >= this.pt_y)
if (isAdd) {
this.$Message.error('签章不在合同内部')
// dom.remove()
this.removeElement(dom)
dom = null
return false
}
if (event.target.parentElement && this.sealWidth) {
dom.children[1].addEventListener('click', () => {
this.iconClick()
})
event.target.parentElement.appendChild(dom)
} else {
console.log(event)
}
},
allowDrop (event) {
event.preventDefault()
},
selectItem (index) {
this.selectPage = index
let step = this.$refs.docContainer.scrollHeight / this.imgList.length
this.$refs.docContainer.scrollTop = index * step
},
getSubmitData () {
let data = {
'locationX': '250',
'locationY': '380',
'offsetX': '100',
'offsetY': '250',
'signMode': '1',
'signOnPage': '1'
}
if (data) {
return this.signaturelocation
}
}
}
}
</script>
<style lang='scss' scoped>
.treaty {
width:100%;
.pageList {
padding-top:10px;
text-align: center;
.pageTitle {
text-align: left;
border-bottom:1px solid #E3E3E3;
display:inline-block;
height: 28px;
line-height:28px;
margin-bottom:10px;
width:80px;
}
.pagePanel {
height: calc(100vh - #{$web_header_height} - 176px);
text-align: center;
overflow: auto;
overflow-x: hidden;
.item {
width: 78px;
height: 92px;
text-align: center;
margin: 0px auto 8px auto;
border: 1px solid #EBEBEB;
position: relative;
img {
width: 100%;
height: 100%;
float:left;
}
> p {
position: absolute;
top:73px;
left:0;
float:left;
text-align: center;
width:100%;
height:18px;
font-size: 12px;
color: #FFFFFF;
letter-spacing: 0;
opacity: 0.4;
background: #626262;
}
&.on {
border: 1px solid #92D2CC;
>p{
opacity: 0.5;
background: #24A599;
}
}
}
}
}
.imgListpanel{
height: calc(100vh - #{$web_header_height} - 132px);
text-align:center;
overflow:auto;
overflow-x:hidden;
.imgpanel{
width:100%;
margin:0 auto
}
.imgList{
display:inline-block;
zoom: 1;
text-align: center;
position: relative;
width:100%;
float:left;
img{
width: 100%;
height: 100%;
float:left;
}
.dropDom{
padding: 15px 25px;
background: transparent;
border:1px solid #EDEDED;
>span{
width: 30px;
height: 30px;
position: absolute;
right: -15px;
top: -12px;
font-size: 16px;
}
}
}
}
.dropDom{
padding: 15px 25px;
background:#fff;
>span{
display:none
}
}
.rightPanel {
height: calc(100vh - #{$web_header_height} - 132px);
overflow:auto;
overflow-x:hidden;
padding: 10px 18px;
.item {
min-height:100px;
.title {
font-size: 14px;
color: #292929;
width: 100%;
border-bottom: 1px solid #E3E3E3;
display: inline-block;
height: 30px;
line-height:30px;
margin-bottom: 10px;
}
p {
font-size: 12px;
color: #666666;
padding-bottom: 5px;
&.fic {
color: #999;
text-align: center;
}
}
.line{
height:10px;
border-bottom: 1px solid #E3E3E3;
margin-bottom: 10px;
}
span{
font-size: 12px;
color:#999;
float:right;
&.on { color: #4ED5A4; }
}
}
}
}
</style>
更多推荐
已为社区贡献5条内容
所有评论(0)