开发项目时,碰到了一个这样的需求:需要前端从服务器获取到一个pdf文件,然后用户通过手动拖拽公章的方式确认公章应该加盖的位置,然后将位置信息传递给服务器。代码并不是非常完美,仅供大家参考。
引用的两个包版本为
"vue-pdf-embed": "^1.1.5",
"vue3-pdfjs": "^0.1.6",
全部代码实现如下:(为了方便大家查看效果,pdf由用户手动上传得到)
<template>
<div>
<!-- 本地上传一个pdf文件用于测试 -->
<input type="file" id="file-input" multiple />
<div style="position: relative; margin: 0 auto">
<div class="control">
<span>共{{ numPages }}页</span>
<el-pagination
v-model:current-page="pageNum"
:page-size="1"
background
layout="prev, pager, next, jumper"
:total="numPages"
/>
</div>
<div class="all" id="wrap">
<!-- 给拖拽存放区添加拖拽存放区事件 -->
<div class="drag" @drop="drop" @dragover.prevent ref="vuepdf">
<vue-pdf-embed
:source="source"
class="vue-pdf-embed"
width="700"
:style="scaleFun"
:page="pageNum"
/>
<div
:class="['box', item.selectName]"
@mousedown="move($event, item)"
@contextmenu.prevent="deleteModel(index)"
@click="getModel(item)"
v-for="(item, index) in dropData"
:key="index"
:style="{ left: item.left + 'px', top: item.top + 'px' }"
>
{{ item.name }}
{{ item.model }}
</div>
</div>
</div>
</div>
<div>
<el-button
type="success"
draggable="true"
@dragstart="dragstart($event, item)"
@dragend="dragend"
v-for="(item, index) in dragList"
:key="index"
>盖章</el-button
>
<el-button type="primary" @click="submit">保存</el-button>
<p>请拖拽《盖章》按钮到合同区域,然后移动公章至合适位置,点击《保存》后盖章成功。</p>
</div>
</div>
</template>
<script setup>
/**
*@date: 2024-08-16
*@description: 合同签署-签章
**/
import VuePdfEmbed from 'vue-pdf-embed'
import { createLoadingTask } from 'vue3-pdfjs/esm' // 获得总页数
import { getCurrentInstance, ref, computed, onMounted } from 'vue'
import { ElButton } from 'element-plus'
const { proxy } = getCurrentInstance()
let dropData = ref([])
let dragList = ref([
{
name: '公章处1',
width: 50,
height: 50,
left: 0,
top: 0,
type: 4,
model: ''
}
])
let currentItem = ref({})
let x = ref('')
let y = ref('')
let height = ref(0)
// 内部移动事件
const move = (e, item) => {
const odiv = e.target
const disX = e.clientX - odiv.offsetLeft
const disY = e.clientY - odiv.offsetTop
document.onmousemove = (e) => {
let left = e.clientX - disX
let top = e.clientY - disY
if (top <= 0) {
top = 0
}
if (top >= height.value - 100) {
top = height.value - 100
}
if (left >= 700 - 100) {
left = 700 - 100
}
if (left <= 0) {
left = 0
}
// 拖拽位置设定
item.left = left
item.top = top
// 获取位置
// 500 容器高度 50 印章一半距离
x.value = left + 50
y.value = top + 50
}
document.onmouseup = (e) => {
document.onmousemove = null
document.onmouseup = null
}
}
const dragstart = (e, item) => {
e.dataTransfer.setData('item', JSON.stringify(item))
if (height.value === 0) {
height.value = proxy.$refs['vuepdf'].offsetHeight
}
}
const dragend = (e) => {
e.dataTransfer.clearData()
}
// 拖拽事件,获取参数
const drop = (e) => {
// 获取拖拽对象的参数
let data = e.dataTransfer.getData('item')
data = JSON.parse(data)
// 鼠标落点位置相对于当前拖拽存放地的 x y
data.left = e.layerX - 50
data.top = e.layerY - 50
// data.top = document.querySelector("#wrap").scrollTop;
// 存放到当前拖拽对象生成的列表中
// dropData.value.push(data)
dropData.value[0] = data //只允许存在一个公章,每次拖拽后都顶替掉第一个章
}
const deleteModel = (index) => {
dropData.value.splice(index, 1)
}
// 获取参数
const getModel = (item) => {
dropData.value = dropData.value.map((item) => {
item.selectName = ''
return item
})
item.selectName = 'select'
currentItem.value = item
}
//合同初始化,页数
let source = ref('')
let scale = ref(1)
let pageNum = ref(1)
let numPages = ref(0)
const scaleFun = computed(() => {
return `transform:scale(${scale.value})`
})
const submit = () => {
console.log(x.value, y.value)
}
const init = () => {
document.getElementById('file-input').addEventListener('change', function (e) {
let fileList = e.target.files
let file = fileList[0]
source.value = URL.createObjectURL(file)
//获取pdf总页数
const loadingTask = createLoadingTask(source.value)
loadingTask.promise.then((pdf) => {
numPages.value = pdf.numPages
})
})
}
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>
.div {
bottom: 20px;
position: absolute;
left: 50%;
margin-left: -152px;
}
.vue-pdf-embed {
text-align: center;
}
.all {
overflow-y: scroll;
height: 650px;
}
.drag {
overflow: hidden;
width: 700px;
height: auto;
margin: 0 auto;
position: relative;
.box {
position: absolute;
top: 0px;
left: 0px;
width: 100px;
height: 100px;
border-radius: 50%;
background-size: 100% 100%;
border: 3px solid red;
user-select: none;
cursor: pointer;
text-align: center;
line-height: 94px;
color: red;
font-weight: bolder;
}
.select {
border-color: red;
}
}
p {
font-size: 13px;
color: red;
margin: 2px 0 0 0;
}
.control {
display: flex;
justify-content: center;
span {
line-height: 36px;
}
}
</style>