当前位置: 首页 > news >正文

做防伪的网站海外建站

做防伪的网站,海外建站,企业网站建设模块,网站开发验收确 认书一、前情提要 1. 需求 仿照e签宝,实现pdf电子签章 > 拿到pdf链接,移动章的位置,获取章的坐标 技术 : 使用fabric pdfjs-dist vuedraggable 2. 借鉴 一位大佬的代码仓亏 : 地址 一位大佬写的文章 :地址 3. 优化 在大佬的代码…

一、前情提要

1. 需求

仿照e签宝,实现pdf电子签章 => 拿到pdf链接,移动章的位置,获取章的坐标

技术 : 使用fabric + pdfjs-dist + vuedraggable

2. 借鉴

一位大佬的代码仓亏 : 地址

一位大佬写的文章 :地址

3. 优化

在大佬的代码基础上,进行了些许优化,变的更像e签宝

二、下载

ps : 怕版本不同,导致无法运行,请下载指定版本

1. fabric

fabric : 是一个功能强大且操作简单的 Javascript HTML5 canvas 工具库

npm install fabric@5.3.0

2. pdfjs-dist

npm install pdfjs-dist@2.5.207

问题一

注意 : 最好配置一下babel,因为打包的时候可能会报错

因为babel默认不会转化node_modules中的包,但是pdfjs-dist用了es6的东东

// 安装包
npm install babel-loader @babel/core @babel/preset-env -D

在webpack.config.js中配置

{test: /\.js$/,loader: 'babel-loader',include: [resolve('src'),// 转化pdfjs-dist,之所以分开写,是因为pdfjs-dist里面有很多es6的语法,但是我们只需要转化pdfjs-dist里面的web文件夹下的js文件resolve('node_modules/pdfjs-dist/web/pdf_viewer.js'),resolve('node_modules/pdfjs-dist/build/pdf.js'),resolve('node_modules/pdfjs-dist/build/pdf.worker.js'),resolve('node_modules/pdfjs-dist/build/pdf.worker.entry.js')        ]
},

问题二 

pdf.js文件过大,可以给 .babelrc 加上属性,"compact": false

3. vuedraggable

npm install vuedraggable@2.24.3

三、代码

1. 准备pdf文件

text.pdf 可放置在 src/static 文件夹中

ps : 线上最好让后端返回pdf链接,因为存在pdf跨域问题

2. 大佬的代码

<!-- //?模块说明 =>  合同签章模块 -->
<template><div id="elesign" class="elesign"><el-row><el-col :span="4" style="margin-top: 1%"><div class="left-title">我的印章</div><draggablev-model="mainImagelist":group="{ name: 'itext', pull: 'clone' }":sort="false"@end="end"><transition-group type="transition"><li v-for="item in mainImagelist" :key="item" class="item" style="text-align: center"><img :src="item" width="100%;" height="100%" class="imgstyle" /></li></transition-group></draggable></el-col><el-col :span="16" style="text-align: center" class="pCenter"><div class="page"><!-- <el-button class="btn-outline-dark" @click="zoomIn">-</el-button><span style="color: red">{{ (percentage * 100).toFixed(0) + '%' }}</span><el-button class="btn-outline-dark" @click="zoomOut">+</el-button> --><el-button class="btn-outline-dark" @click="prevPage">上一页</el-button><el-button class="btn-outline-dark" @click="nextPage">下一页</el-button><el-button class="btn-outline-dark">{{ pageNum }}/{{ numPages }}页</el-button><el-input-numberstyle="margin: 0 5px; border-radius: 5px"class="btn-outline-dark"v-model="pageNum":min="1":max="numPages"label="输入页码"></el-input-number><el-button class="btn-outline-dark" @click="cutover">跳转</el-button></div><canvas id="the-canvas" /><!-- 盖章部分 --><canvas id="ele-canvas"></canvas><div class="ele-control" style="margin-bottom: 2%"><el-button class="btn-outline-dark" @click="removeSignature">删除签章</el-button><el-button class="btn-outline-dark" @click="clearSignature">清除所有签章</el-button><el-button class="btn-outline-dark" @click="submitSignature">提交所有签章信息</el-button></div></el-col><el-col :span="4" style="margin-top: 1%"><div class="left-title">任务信息</div><div style="text-align: center"><div><div class="right-item"><div class="right-item-title">文件主题</div><div class="detail-item-desc">{{ taskInfo.title }}</div></div><div class="right-item"><div class="right-item-title">发起方</div><div class="detail-item-desc">{{ taskInfo.uname }}</div></div><div class="right-item"><div class="right-item-title">截止时间</div><div class="detail-item-desc">{{ taskInfo.endtime }}</div></div></div></div></el-col></el-row></div>
</template>
<script>
import draggable from 'vuedraggable';
import { fabric } from 'fabric';
import workerSrc from 'pdfjs-dist/es5/build/pdf.worker.entry';
import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer';
const pdfjsLib = require('pdfjs-dist/es5/build/pdf.js');
pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;
export default {components: { draggable },data() {return {// pdf预览pdfUrl: '',pdfDoc: null,numPages: 1,pageNum: 1,scale: 2.2,pageRendering: false,pageNumPending: null,sealUrl: '',signUrl: '',canvas: null,ctx: null,canvasEle: null,whDatas: null,mainImagelist: [],taskInfo: {}// percentage: 1};},computed: {hasSigna() {if (this.canvasEle && this.canvasEle.getObjects()[0]) {return true;} else {return false;}}},created() {var that = this;that.mainImagelist = [require('@/assets/img/projectCenter/sign.png'), require('@/assets/img/projectCenter/seal.png')];that.taskInfo = { title: '测试盖章', uname: '张三', endtime: '2021-09-01 17:59:59' };this.setPdfArea();},mounted() {// this.showpdf(this.pdfUrl);if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {// eslint-disable-next-line no-alertalert('Please build the pdfjs-dist library using\n  `gulp dist-install`');}},methods: {// pdf预览// zoomIn() {//   console.log('缩小');//   if (this.scale <= 0.5) {//     this.$message.error('已经显示最小比例');//   } else {//     this.scale -= 0.1;//     this.percentage -= 0.1;//     this.renderPage(this.pageNum);//     this.renderFabric();//   }// },// zoomOut() {//   console.log('放大');//   if (this.scale >= 2.2) {//     this.$message.error('已经显示最大比例');//   } else {//     this.scale += 0.1;//     this.percentage += 0.1;//     this.renderPage(this.pageNum);//     this.renderFabric();//   }// },renderPage(num) {let _this = this;this.pageRendering = true;return this.pdfDoc.getPage(num).then((page) => {let viewport = page.getViewport({ scale: _this.scale }); // 设置视口大小_this.canvas.height = viewport.height;_this.canvas.width = viewport.width;// Render PDF page into canvas contextlet renderContext = {canvasContext: _this.ctx,viewport: viewport};let renderTask = page.render(renderContext);// Wait for rendering to finishrenderTask.promise.then(() => {_this.pageRendering = false;if (_this.pageNumPending !== null) {// New page rendering is pendingthis.renderPage(_this.pageNumPending);_this.pageNumPending = null;}});});},queueRenderPage(num) {if (this.pageRendering) {this.pageNumPending = num;} else {this.renderPage(num);}},prevPage() {this.confirmSignature();if (this.pageNum <= 1) {return;}this.pageNum--;},nextPage() {this.confirmSignature();if (this.pageNum >= this.numPages) {return;}this.pageNum++;},cutover() {this.confirmSignature();},// 渲染pdf,到时还会盖章信息,在渲染时,同时显示出来,不应该在切换页码时才显示印章信息showpdf(pdfUrl) {let caches = JSON.parse(localStorage.getItem('signs')); // 获取缓存字符串后转换为对象// console.log(caches);if (caches != null) {let datas = caches[this.pageNum];if (datas != null && datas != undefined) {for (let index in datas) {this.addSeal(datas[index].sealUrl,datas[index].left,datas[index].top,datas[index].index);}}}this.canvas = document.getElementById('the-canvas');this.ctx = this.canvas.getContext('2d');pdfjsLib.getDocument({ url: pdfUrl, rangeChunkSize: 65536, disableAutoFetch: false }).promise.then((pdfDoc_) => {this.pdfDoc = pdfDoc_;this.numPages = this.pdfDoc.numPages;this.renderPage(this.pageNum).then(() => {this.renderPdf({width: this.canvas.width,height: this.canvas.height});});this.commonSign(this.pageNum, true);});},/***  盖章部分开始*/// 设置绘图区域宽高renderPdf(data) {this.whDatas = data;// document.querySelector("#elesign").style.width = data.width + "px";},// 生成绘图区域renderFabric() {let canvaEle = document.querySelector('#ele-canvas');let pCenter = document.querySelector('.pCenter');canvaEle.width = pCenter.clientWidth;// canvaEle.height = (this.whDatas.height)*(this.scale);canvaEle.height = this.whDatas.height;this.canvasEle = new fabric.Canvas(canvaEle);let container = document.querySelector('.canvas-container');container.style.position = 'absolute';container.style.top = '50px';// container.style.left = "30%";},// 相关事件操作哟canvasEvents() {// 拖拽边界 不能将图片拖拽到绘图区域外this.canvasEle.on('object:moving', function (e) {var obj = e.target;// if object is too big ignoreif (obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width) {return;}obj.setCoords();// top-left  cornerif (obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0) {obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top);obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left);}// bot-right cornerif (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height ||obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width) {obj.top = Math.min(obj.top,obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top);obj.left = Math.min(obj.left,obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left);}});},// 添加公章addSeal(sealUrl, left, top, index) {fabric.Image.fromURL(sealUrl, (oImg) => {oImg.set({left: left,top: top,// angle: 10,scaleX: 0.8,scaleY: 0.8,index: index});// oImg.scale(0.5); //图片缩小一this.canvasEle.add(oImg);});},// 删除签章removeSignature() {this.canvasEle.remove(this.canvasEle.getActiveObject());},// 翻页展示盖章信息commonSign(pageNum, isFirst = false) {if (isFirst == false) this.canvasEle.remove(this.canvasEle.clear()); // 清空页面所有签章let caches = JSON.parse(localStorage.getItem('signs')); // 获取缓存字符串后转换为对象// console.log(caches);if (caches == null) return false;let datas = caches[this.pageNum];if (datas != null && datas != undefined) {for (let index in datas) {this.addSeal(datas[index].sealUrl,datas[index].left,datas[index].top,datas[index].index);}}},// 确认签章位置并保存到缓存confirmSignature() {let data = this.canvasEle.getObjects(); // 获取当前页面内的所有签章信息let caches = JSON.parse(localStorage.getItem('signs')); // 获取缓存字符串后转换为对象let signDatas = {}; // 存储当前页的所有签章信息let i = 0;// let sealUrl = '';for (var val of data) {signDatas[i] = {width: val.width,height: val.height,top: val.top,left: val.left,angle: val.angle,translateX: val.translateX,translateY: val.translateY,scaleX: val.scaleX,scaleY: val.scaleY,pageNum: this.pageNum,sealUrl: this.mainImagelist[val.index],index: val.index};i++;}if (caches == null) {caches = {};caches[this.pageNum] = signDatas;} else {caches[this.pageNum] = signDatas;}localStorage.setItem('signs', JSON.stringify(caches)); // 对象转字符串后存储到缓存},// 提交数据submitSignature() {this.confirmSignature();// let caches = localStorage.getItem('signs');// console.log(JSON.parse(caches));return false;},// 清空数据clearSignature() {this.canvasEle.remove(this.canvasEle.clear()); // 清空页面所有签章localStorage.removeItem('signs'); // 清除缓存},end(e) {this.addSeal(this.mainImagelist[e.newDraggableIndex],e.originalEvent.layerX,e.originalEvent.layerY,e.newDraggableIndex);},// 设置PDF预览区域高度setPdfArea() {this.pdfUrl = './static/text.pdf';// this.pdfurl = res.data.data.pdfurl;this.$nextTick(() => {this.showpdf(this.pdfUrl); // 接口返回的应该还有盖章信息,不只是pdf});}},watch: {whDatas: {handler() {const loading = this.$loading({lock: true,text: 'Loading',spinner: 'el-icon-loading',background: 'rgba(0, 0, 0, 0.7)'});if (this.whDatas) {// console.log(this.whDatas);loading.close();this.renderFabric();this.canvasEvents();let eleCanvas = document.querySelector('#ele-canvas');eleCanvas.style = 'border:1px solid #5ea6ef;margin-top: 10px;';}}},pageNum: function () {this.commonSign(this.pageNum);this.queueRenderPage(this.pageNum);}}
};
</script>
<style lang="scss" scoped>
/*pdf部分*/
#the-canvas {margin-top: 10px;
}html:fullscreen {background: white;
}
.elesign {display: flex;flex: 1;flex-direction: column;position: relative;/* padding-left: 180px; */margin: auto;/* width:600px; */
}
.page {text-align: center;margin: 0 auto;margin-top: 1%;
}
#ele-canvas {/* border: 1px solid #5ea6ef; */overflow: hidden;
}
.ele-control {text-align: center;margin-top: 3%;
}
#page-input {width: 7%;
}@keyframes ani-demo-spin {from {transform: rotate(0deg);}50% {transform: rotate(180deg);}to {transform: rotate(360deg);}
}
/* .loadingclass{position: absolute;top:30%;left:49%;z-index: 99;
} */
.left {position: absolute;top: 42px;left: -5px;padding: 5px 5px;/*border: 1px solid #eee;*//*border-radius: 4px;*/
}
.left-title {text-align: center;padding-bottom: 10px;border-bottom: 1px solid #eee;
}
li {list-style-type: none;padding: 10px;
}
.imgstyle {vertical-align: middle;width: 130px;border: solid 1px #e8eef2;background-image: url('~@/assets/img/projectCenter/tuo.png');background-repeat: no-repeat;
}
.right {position: absolute;top: 7px;right: -177px;margin-top: 34px;padding-top: 10px;padding-bottom: 20px;width: 152px;/*border: 1px solid #eee;*//*border-radius: 4px;*/
}
.right-item {margin-bottom: 15px;margin-left: 10px;
}
.right-item-title {color: #777;height: 20px;line-height: 20px;font-size: 12px;font-weight: 400;text-align: left !important;
}
.detail-item-desc {color: #333;line-height: 20px;width: 100%;font-size: 12px;display: inline-block;text-align: left;
}
.btn-outline-dark {color: #0f1531;background-color: transparent;background-image: none;border: 1px solid #3e4b5b;
}.btn-outline-dark:hover {color: #fff;background-color: #3e4b5b;border-color: #3e4b5b;
}
</style>

3. 优化后的代码 

<!-- //?模块说明 =>  合同签章模块 addToTab-->
<template><div class="contract-signature-view"><div class="title-operation"><h2 class="title">合同签章</h2><div class="operation"><el-button type="danger" @click="removeSignature">删除签章</el-button><el-button type="danger" @click="clearSignature">清空签章</el-button><el-button type="primary" @click="submitSignature">提交签章</el-button></div></div><div class="section-box"><!-- 签章图片 --><aside class="signature-img"><div class="info"><h3 class="name">印章</h3><p class="text">将示例印章标识拖到文件相应区域即可获取签章位置</p></div><!-- 拖拽 --><draggablev-model="mainImagelist":group="{ name: 'itext', pull: 'clone' }":sort="false"@end="end"><transition-group type="transition"><liv-for="item in mainImagelist":key="item.img"class="item"style="text-align: center"><img :src="item.img" width="100%;" height="100%" class="img" /></li></transition-group></draggable></aside><!-- 主体区域 --><section class="main-layout" :class="{ 'is-first': isFirst }"><!-- 操作 --><div class="operate-box"><div class="slider-box"><el-sliderclass="slider"v-model="scale":min="0.5":max="2":step="0.1":show-tooltip="false"@change="sliderChange"/><span class="scale-value">{{ (scale * 100).toFixed(0) + '%' }}</span></div><div class="page-change"><i class="icon el-icon-arrow-left" @click="prevPage" /><!-- :min="1" --><el-inputclass="input-box"v-model.number="pageNum":max="defaultNumPages"@change="cutover"/><span class="default-text">/{{ defaultNumPages }}</span><i class="icon el-icon-arrow-right" @click="nextPage" /></div></div><!-- 画图 --><div class="out-view" :class="{ 'is-show': isShowPdf }"><div class="canvas-layout" v-for="item in numPages" :key="item"><!-- pdf部分 --><canvas class="the-canvas" /><!-- 盖章部分 --><canvas class="ele-canvas"></canvas></div></div><i class="loading" v-loading="!isShowPdf" /></section><!-- 位置信息 --><div class="position-info"><h3 class="title">位置信息</h3><ul class="nav"><li class="item" v-for="(item, index) in coordinateList" :key="index"><span>{{ item.name }}</span><span>{{ item.page }}</span><span>{{ item.left }}</span><span>{{ item.top }}</span></li></ul></div></div></div>
</template>
<script>
// 拖拽插件
import draggable from 'vuedraggable';
// pdf插件
import { fabric } from 'fabric';
import workerSrc from 'pdfjs-dist/es5/build/pdf.worker.entry';
const pdfjsLib = require('pdfjs-dist/es5/build/pdf.js');
pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;export default {components: { draggable },data() {return {// pdf地址pdfUrl: '',// 左侧签章列表mainImagelist: [],// 右侧坐标数据coordinateList: [{ name: '名称', page: '所在页面', left: 'x坐标', top: 'Y坐标' }],// 总页数numPages: 1,defaultNumPages: 1,// 当前页pageNum: 1,// 缩放比例scale: 1,// pdf是否显示isFirst: true,isShowPdf: false,// pdf最外层的out-viewoutViewDom: null,// 各页pdf的canvas-layoutcanvasLayoutTopList: [],// 用来签章的canvas数组canvasEle: [],// 绘图区域的宽高whDatas: null,// pdf渲染的canvas数组canvas: [],// pdf渲染的canvas的ctx数组ctx: [],// pdf渲染的canvas的宽高pdfDoc: null,// 隐藏的input,用来提交数据shadowInputValue: ''};},created() {this.mainImagelist = [{ name: '印章', img: require('@/assets/img/projectCenter/contract-sign-img.png') }// { name: '印章', img: require('./sign.png') },// { name: '红章', img: require('@/assets/img/projectCenter/seal.png') }];this.setPdfArea();},mounted() {},methods: {/*** pdf相关部分*/// 设置PDF地址setPdfArea() {// // 1. 获取地址栏// const urlString = window.location.href;// // 2. 截取地址栏// const pdfStr = urlString.split('?')[1];// // 3. 截取pdf地址并解码// this.pdfUrl = decodeURIComponent(pdfStr.split('=')[1]);this.pdfUrl = './static/text.pdf';this.$nextTick(() => {this.showpdf(this.pdfUrl); // 接口返回的应该还有盖章信息,不只是pdf});},// 解析pdfshowpdf(pdfUrl) {pdfjsLib.getDocument({ url: pdfUrl, rangeChunkSize: 65536, disableAutoFetch: false }).promise.then((pdfDoc_) => {this.pdfDoc = pdfDoc_;this.numPages = this.pdfDoc.numPages;this.defaultNumPages = this.pdfDoc.numPages;this.$nextTick(() => {this.canvas = document.querySelectorAll('.the-canvas');this.canvas.forEach((item) => {this.ctx.push(item.getContext('2d'));});// 循环渲染pdffor (let i = 1; i <= this.numPages; i++) {this.renderPage(i).then(() => {this.renderPdf({width: this.canvas[i - 1].width,height: this.canvas[i - 1].height});});}setTimeout(() => {this.renderFabric();this.canvasEvents();}, 1000);});});},// 设置pdf宽高,缩放比例,渲染pdfrenderPage(num) {// console.log('this.canvas', this.canvas[num], num);return this.pdfDoc.getPage(num).then((page) => {const viewport = page.getViewport({ scale: this.scale }); // 设置视口大小this.canvas[num - 1].height = viewport.height;this.canvas[num - 1].width = viewport.width;// Render PDF page into canvas contextconst renderContext = {canvasContext: this.ctx[num - 1],viewport: viewport};page.render(renderContext);});},// 设置绘图区域宽高renderPdf(data) {this.whDatas = data;},// 生成绘图区域renderFabric() {// 1. 拿到全部的canvas-layoutconst canvasLayoutDom = document.querySelectorAll('.canvas-layout');// 2. 循环遍历canvasLayoutDom.forEach((item) => {this.canvasLayoutTopList.push({ obj: item, top: item.offsetTop });// 3. 设置宽高和居中item.style.width = this.whDatas.width + 'px';item.style.height = this.whDatas.height + 'px';item.style.margin = '0 auto 18px';item.style.boxShadow = '4px 4px 4px #e9e9e9';// 4. 拿到盖章canvasconst canvasEle = item.querySelector('.ele-canvas');// 5. 拿到pdf的canvasconst pCenter = item.querySelector('.the-canvas');// 6. 设置盖章canvas的宽高canvasEle.width = pCenter.clientWidth;canvasEle.height = this.whDatas.height;// 7. 创建fabric对象并存储this.canvasEle.push(new fabric.Canvas(canvasEle));// 8. 设置盖章canvas的样式const container = item.querySelector('.canvas-container');container.style.position = 'absolute';container.style.left = '50%';container.style.transform = 'translateX(-50%)';container.style.top = '0px';});// 现形this.isFirst = false;this.isShowPdf = true;this.outViewDom = document.querySelector('.out-view');// 开启监听窗口滚动this.outViewScroll();},// 开启监听窗口滚动outViewScroll() {this.outViewDom.addEventListener('scroll', this.outViewRun);},// 关闭监听窗口滚动outViewScrollClose() {this.outViewDom.removeEventListener('scroll', this.outViewRun);},// 窗口滚动outViewRun() {const scrollTop = this.outViewDom.scrollTop;const topList = this.canvasLayoutTopList.map((item) => item.top);// 增加一个最大值topList.push(Number.MAX_SAFE_INTEGER);for (let index = 0; index < topList.length; index++) {const element = topList[index];if (element <= scrollTop && scrollTop < topList[index + 1]) {this.pageNum = index + 1;break;}}},// scale滑块,重新渲染整个pdfsliderChange() {this.pageNum = 1;this.numPages = 0;this.canvasLayoutTopList = [];this.canvasEle = [];this.ctx = [];this.canvas = [];this.isShowPdf = false;// this.outViewScrollClose();this.whDatas = null;this.coordinateList = [{ name: '名称', page: '所在页面', left: 'x坐标', top: 'Y坐标' }];this.getSignatureJson();setTimeout(() => {this.numPages = this.pdfDoc.numPages;this.$nextTick(() => {this.canvas = document.querySelectorAll('.the-canvas');this.canvas.forEach((item) => {this.ctx.push(item.getContext('2d'));});// 循环渲染pdffor (let i = 1; i <= this.numPages; i++) {this.renderPage(i).then(() => {this.renderPdf({width: this.canvas[i - 1].width,height: this.canvas[i - 1].height});});}setTimeout(() => {this.renderFabric();this.canvasEvents();}, 1000);});}, 1000);},/*** 签章相关部分*/// 签章拖拽边界处理,不能将图片拖拽到绘图区域外canvasEvents() {this.canvasEle.forEach((item) => {item.on('object:moving', (e) => {const obj = e.target;// if object is too big ignoreif (obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width) {return;}obj.setCoords();// top-left  cornerif (obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0) {obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top);obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left);}// bot-right cornerif (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height ||obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width) {obj.top = Math.min(obj.top,obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top);obj.left = Math.min(obj.left,obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left);}// console.log('obj.cacheKey',obj.cacheKey);const findIndex = this.coordinateList.slice(1).findIndex((coord) => coord.cacheKey == obj.cacheKey);const keys = ['width', 'height', 'top', 'left', 'angle', 'scaleX', 'scaleY'];keys.forEach((item) => {this.coordinateList[findIndex + 1][item] = Math.ceil(obj[item] / this.scale);});this.getSignatureJson();});});},// 拖拽结束end(e) {// 找到当前拖拽到哪一个canvas-layout上const currentCanvasLayout = e.originalEvent.target.parentElement.parentElement;const findIndex = this.canvasLayoutTopList.findIndex((item) => item.obj == currentCanvasLayout);if (findIndex == -1) return false;// 取整const left = e.originalEvent.layerX < 0 ? 0 : Math.ceil(e.originalEvent.layerX / this.scale);const top = e.originalEvent.layerY < 0 ? 0 : Math.ceil(e.originalEvent.layerY / this.scale);// console.log('e', e, findIndex);this.addSeal({sealUrl: this.mainImagelist[e.newDraggableIndex].img,left,top,index: e.newDraggableIndex,pageNum: findIndex});},// 添加公章addSeal({ sealUrl, left, top, index, pageNum }) {fabric.Image.fromURL(sealUrl, (oImg) => {oImg.set({// 距离左边的距离left: left,// 距离顶部的距离top: top,// 角度// angle: 10,// 缩放比例,需要乘以scalescaleX: 0.8 * this.scale,scaleY: 0.8 * this.scale,index,// 禁止缩放lockScalingX: true,lockScalingY: true,// 禁止旋转lockRotation: true});this.canvasEle[pageNum].add(oImg);// 保存签章信息this.saveSignature({ pageNum, index, sealUrl });});// this.removeActive();},// 保存签章saveSignature({ pageNum, index, sealUrl }) {// 1. 拿到当前签章的信息let length = 0;let pageConfig = this.coordinateList.filter((item) => item.page - 1 == pageNum);if (pageConfig) length = pageConfig.length;const currentSignInfo = this.canvasEle[pageNum].getObjects()[length];// 2. 拼接数据const keys = ['width', 'height', 'top', 'left', 'angle', 'scaleX', 'scaleY'];const obj = {};keys.forEach((item) => {obj[item] = Math.ceil(currentSignInfo[item] / this.scale);});obj.cacheKey = currentSignInfo.cacheKey;obj.sealUrl = sealUrl;obj.index = index;obj.name = `${this.mainImagelist[index].name}${this.coordinateList.length}`;obj.page = pageNum + 1;this.coordinateList.push(obj);this.getSignatureJson();},// 签章生成json字符串getSignatureJson() {// 1. 判断是否有签章if (this.coordinateList.length <= 1) return (this.shadowInputValue = '');// 2. 拿到签章的信息,去除第一条const signatureList = this.coordinateList.slice(1);// 3. 拼接数据,只要left和top和pageconst keys = ['page', 'left', 'top'];const arr = [];signatureList.forEach((item) => {const obj = {};keys.forEach((key) => {obj[key] = item[key];});arr.push(obj);});// 4. 转成json字符串this.shadowInputValue = JSON.stringify(arr);},/*** 操作相关部分*/// 上一页prevPage() {if (this.pageNum <= 1) return;this.pageNum--;// 滚动到指定位置this.outViewDom.scrollTop = this.canvasLayoutTopList[this.pageNum - 1].top;},// 下一页nextPage() {if (this.pageNum >= this.numPages) return;this.pageNum++;// 滚动到指定位置this.outViewDom.scrollTop = this.canvasLayoutTopList[this.pageNum - 1].top;},// 切换页码cutover() {this.outViewScrollClose();if (this.pageNum < 1) {this.pageNum = 1;} else if (this.pageNum > this.numPages) {this.pageNum = this.numPages;}// 滚动到指定位置this.outViewDom.scrollTop = this.canvasLayoutTopList[this.pageNum - 1].top;setTimeout(() => {this.outViewScroll();}, 500);},// 删除所有的签章选中状态removeActive() {this.canvasEle.forEach((item) => {item.discardActiveObject().renderAll();});},// 删除签章removeSignature() {// 1. 判断是否有选中的签章const findItem = this.canvasEle.filter((item) => item.getActiveObject());// 2. 判断选中签章的个数if (findItem.length == 0) return this.$message.error('请选择要删除的签章');// 3. 判断选中签章的个数是否大于1if (findItem.length > 1) {this.removeActive();return this.$message.error('只能选择删除一个签章,请重新选择');}// 4. 拿到选中的签章的cacheKeyconst activeObj = findItem[0].getActiveObject();const findIndex = this.coordinateList.findIndex((item) => item.cacheKey == activeObj.cacheKey);// 5. 删除选中的签章findItem[0].remove(activeObj);// 6. 删除选中的签章的信息this.coordinateList.splice(findIndex, 1);this.getSignatureJson();},// 清空签章clearSignature() {this.canvasEle.forEach((item) => {item.clear();});this.coordinateList = [{ name: '名称', page: '所在页面', left: 'x坐标', top: 'Y坐标' }];this.getSignatureJson();},// 提交数据submitSignature() {console.log('this.coordinateList', this.coordinateList);}}
};
</script>
<style lang="scss" scoped>
.contract-signature-view {/*pdf部分*/.ele-canvas {overflow: hidden;}.title-operation {height: 80px;padding: 20px 40px;display: flex;align-items: center;justify-content: space-between;.title {font-size: 20px;font-weight: 600;}border-bottom: 1px solid #e4e4e4;}.section-box {position: relative;display: flex;height: calc(100vh - 60px);.signature-img {width: 240px;min-width: 240px;background-color: #fff;padding: 40px 15px;border-right: 1px solid #e4e4e4;.info {margin-bottom: 38px;.name {font-size: 18px;font-weight: 600;color: #000000;line-height: 25px;margin-bottom: 20px;}.text {font-size: 14px;color: #000000;line-height: 20px;}}.item {padding: 10px;border: 1px dashed rgba(0, 0, 0, 0.3);&:not(:last-child) {margin-bottom: 10px;}.img {vertical-align: middle;width: 120px;background-repeat: no-repeat;}}}.main-layout {flex: 1;background-color: #f7f8fa;position: relative;&.is-first {.operate-box {opacity: 0;}}.operate-box {opacity: 1;position: absolute;top: 0;left: 0;width: 100%;height: 40px;background-color: #fff;border-bottom: 1px solid #e4e4e4;display: flex;justify-content: center;align-items: center;.slider-box {width: 230px;display: flex;justify-content: center;align-items: center;border-left: 1px solid #e4e4e4;border-right: 1px solid #e4e4e4;.slider {width: 120px;}.scale-value {margin-left: 24px;font-size: 16px;color: #000000;line-height: 22px;}}.page-change {display: flex;align-items: center;margin-left: 30px;.icon {cursor: pointer;padding: 0 5px;color: #c1c1c1;}.input-box {border: none;/deep/ .el-input__inner {width: 34px;height: 20px;border: none;padding: 0;text-align: center;border-bottom: 1px solid #e4e4e4;}}.default-text {display: flex;line-height: 22px;margin-right: 5px;}}}.out-view {height: calc(100vh - 100px);margin: 40px auto;overflow-x: auto;overflow-y: auto;padding-top: 20px;text-align: center;opacity: 0;transition: all 0.5s;&.is-show {opacity: 1;}.canvas-layout {position: relative;text-align: center;margin: 0 auto 18px;}}.loading {width: 20px;height: 20px;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 999;/deep/ .el-loading-mask {background-color: transparent;}}}.position-info {width: 355px;min-width: 355px;border-left: 1px solid #e4e4e4;background-color: #fff;padding: 14px 15px;.title {font-size: 14px;font-weight: 400;color: #000000;line-height: 20px;padding-bottom: 18px;}.nav {display: flex;flex-direction: column;.item {display: flex;justify-content: space-between;padding: 10px 0;border-bottom: 1px solid #eee;&:first-child {background-color: #f7f8fa;}span {flex: 1;text-align: center;font-size: 12px;color: #000000;line-height: 20px;}}}}}
}
</style>


文章转载自:
http://dinncomultilead.bkqw.cn
http://dinncooleomargarine.bkqw.cn
http://dinncoirony.bkqw.cn
http://dinncopulmometer.bkqw.cn
http://dinncoclinamen.bkqw.cn
http://dinncosurcease.bkqw.cn
http://dinncobilly.bkqw.cn
http://dinncohypoglossal.bkqw.cn
http://dinncoinnative.bkqw.cn
http://dinncocleanout.bkqw.cn
http://dinncoacceleration.bkqw.cn
http://dinncoration.bkqw.cn
http://dinncopolyunsaturate.bkqw.cn
http://dinncomab.bkqw.cn
http://dinncomichaelmas.bkqw.cn
http://dinncocoquetry.bkqw.cn
http://dinncovolucrary.bkqw.cn
http://dinncobuskin.bkqw.cn
http://dinncovaticinate.bkqw.cn
http://dinncocolloquist.bkqw.cn
http://dinncoostinato.bkqw.cn
http://dinncosnaillike.bkqw.cn
http://dinncomodeling.bkqw.cn
http://dinncogreenstone.bkqw.cn
http://dinncodeforest.bkqw.cn
http://dinncosynchronological.bkqw.cn
http://dinncorenerve.bkqw.cn
http://dinncobalsamiferous.bkqw.cn
http://dinncosmuttiness.bkqw.cn
http://dinncogruziya.bkqw.cn
http://dinncothoracectomy.bkqw.cn
http://dinncoeradiation.bkqw.cn
http://dinncoabidjan.bkqw.cn
http://dinncoresidua.bkqw.cn
http://dinncotagetes.bkqw.cn
http://dinncopesewa.bkqw.cn
http://dinncotutelage.bkqw.cn
http://dinncoberserkly.bkqw.cn
http://dinncosolutizer.bkqw.cn
http://dinncodigitizer.bkqw.cn
http://dinncocuracoa.bkqw.cn
http://dinncocolugo.bkqw.cn
http://dinncointercostal.bkqw.cn
http://dinncoexpansionism.bkqw.cn
http://dinncochristocentrism.bkqw.cn
http://dinncopippip.bkqw.cn
http://dinncodisgusting.bkqw.cn
http://dinncobiochrome.bkqw.cn
http://dinncotailender.bkqw.cn
http://dinncoerp.bkqw.cn
http://dinncomeshwork.bkqw.cn
http://dinncospacing.bkqw.cn
http://dinncobullwork.bkqw.cn
http://dinncodowncourt.bkqw.cn
http://dinncoscholzite.bkqw.cn
http://dinncobrattish.bkqw.cn
http://dinncomissile.bkqw.cn
http://dinncophotoscope.bkqw.cn
http://dinncocarbamino.bkqw.cn
http://dinncogymnastic.bkqw.cn
http://dinncolavender.bkqw.cn
http://dinncoprost.bkqw.cn
http://dinncodubbin.bkqw.cn
http://dinncomonitory.bkqw.cn
http://dinncobayesian.bkqw.cn
http://dinncoviscose.bkqw.cn
http://dinncocapitalintensive.bkqw.cn
http://dinncospuria.bkqw.cn
http://dinncozoometry.bkqw.cn
http://dinncophilharmonic.bkqw.cn
http://dinncoretail.bkqw.cn
http://dinncofaitaccompli.bkqw.cn
http://dinncobailable.bkqw.cn
http://dinnconapped.bkqw.cn
http://dinncopaddlesteamer.bkqw.cn
http://dinncoconjury.bkqw.cn
http://dinnconeuroma.bkqw.cn
http://dinncodiskpark.bkqw.cn
http://dinncoslavocracy.bkqw.cn
http://dinncoakyab.bkqw.cn
http://dinncokarnaugh.bkqw.cn
http://dinncokasha.bkqw.cn
http://dinncoeelpot.bkqw.cn
http://dinncodoored.bkqw.cn
http://dinncochoralist.bkqw.cn
http://dinncoauk.bkqw.cn
http://dinncosulaiman.bkqw.cn
http://dinncodilatant.bkqw.cn
http://dinncofledgeless.bkqw.cn
http://dinncoobstacle.bkqw.cn
http://dinncotzitzis.bkqw.cn
http://dinncoworldward.bkqw.cn
http://dinnconcv.bkqw.cn
http://dinncodispersive.bkqw.cn
http://dinncodeva.bkqw.cn
http://dinncotaxite.bkqw.cn
http://dinncosuspension.bkqw.cn
http://dinncounattended.bkqw.cn
http://dinncodriegh.bkqw.cn
http://dinncopariah.bkqw.cn
http://www.dinnco.com/news/99687.html

相关文章:

  • 免费自己制作网站方法独立网站和平台网站
  • 汉口做网站凡科网免费建站
  • 济宁做网站建设的公司如何进行网站制作
  • 外贸自建站收款通道网页模板免费下载网站
  • java开发手册seo网站排名优化公司哪家好
  • 先做网站主页还是先上架宝贝营销策略有哪些理论
  • 做靓号网站友情链接有什么用
  • 东港网站建设steam交易链接怎么改
  • 网站建设应注重实用性东莞优化seo
  • 河北公司注册网上核名网站seo哪里做的好
  • vs2010网站开发示例微信营销的模式有哪些
  • 毕设做网站惠州seo
  • 音箱厂家东莞网站建设怎么建立信息网站平台
  • 如何用eclipse做网站苏州seo关键词优化排名
  • 网站建设中的主要功能优化网站排名工具
  • 会展中心网站建设seo实战密码第四版
  • 重庆好的网站制作公司哪家好上海网站建设联系方式
  • 怎么推广一个app长沙seo优化哪家好
  • 广州做网站公司培训seo站长论坛
  • WordPress订单功能开发开鲁seo服务
  • 如何用浏览器访问本地的wordpress网站怎么优化推广
  • 阿里巴巴网站详情页怎么做从事网络营销的公司
  • 沧州企业网站优化大连今日新闻头条
  • 自已买域名做网站要多少钱六年级下册数学优化设计答案
  • 用易语言做攻击网站软件下载网站建设的系统流程图
  • 城阳做网站的南宁seo收费
  • 龙岗网站制作公司一般多少钱自助建站网站
  • 建设旅游网站目的西安关键词排名提升
  • 做网站的报价企业网站制作
  • 怎样注册自己网站企业文化经典句子