<!--
* @Description:
* @autor: liuleilei
* @Date: 2025-06-09 13:52:09
* @LastEditors: liuleilei
* @LastEditTime: 2025-06-09 15:10:50
-->
<template>
<div class="drag-sign-seal">
<div id="elesign" class="elesign scrollbar vc-page">
<el-row style="height: 100%">
<div class="page">
<el-button
size="small"
class="btn-outline-default"
@click="prevPage"
>上一页</el-button
>
<el-button
size="small"
class="btn-outline-default"
@click="nextPage"
>下一页</el-button
>
<el-button size="small" class="btn-outline-default"
>{{ pageNum }}/{{ numPages }}页</el-button
>
<el-input-number
size="small"
style="margin-left: 0.1rem; border-radius: 5px"
v-model="currentPage"
:min="1"
:max="numPages"
label="输入页码"
@blur="nullity"
></el-input-number>
<el-button size="small" class="btn-outline-default" @click="cutover"
>跳转</el-button
>
<el-button size="small" class="btn-outline-default" @click="zoomOut">缩小</el-button>
<el-button size="small" class="btn-outline-default">{{ Math.round(currentScale * 100) }}%</el-button>
<el-button size="small" class="btn-outline-default" @click="zoomIn">放大</el-button>
</div>
<div class="col-left-box" id="col-left-box" ref="colLeftBox">
<el-col :span="20" class="pCenter col-left" id="col-left" ref="colLeft">
<div class="canvas-div-box" ref="canvasDivBox">
<div class="canvas-div" id="canvas-div" ref="canvasDiv" v-loading="showLoadingChild">
<!-- pdf部分 -->
<canvas style="width: 100%; height: 100%;" id="the-canvas"/>
<!-- 盖章部分 -->
<canvas style="width: 100%; height: 100%; border: none !important" id="ele-canvas"></canvas>
</div>
</div>
</el-col>
</div>
<el-col :span="4" class="col-right">
<el-button size="small" type="primary" @click="removeSignature">
清除签章</el-button
>
<el-tree
:data="menuTree"
:props="elTreeProps"
default-expand-all
:expand-on-click-node="false"
>
<div slot-scope="{ node, data }">
<div class="custom-tree-node" v-if="!data.pid && !data.isOwnSign">
{{ data.name }}
</div>
<draggable
v-if="data.pid"
class="draggable-list scrollbar"
v-model="mainImagelist"
:group="{ name: 'itext', pull: 'clone' }"
:sort="false"
@end="end($event, data)"
>
<transition-group type="transition">
<template>
<span :key="data.url">
<img
:src="data.url"
width="100%;"
height="100%"
class="imgstyle"
/>
</span>
<div v-if="data.name">{{ data.name }}</div>
</template>
</transition-group>
</draggable>
<div v-if="data.sealName" style="text-align: center;white-space: pre-wrap;">
{{ data.sealName }}
</div>
</div>
</el-tree>
</el-col>
</el-row>
</div>
<!-- 加载中遮罩 -->
<van-overlay :show="showLoadingChild">
<van-loading class="van-loading" style="height: 100%"> </van-loading>
</van-overlay>
</div>
</template>
<script>
import {
getPdfFile,
findSignListByUserId,
findCanUseSealList,
getFileServerConfig,
} from "@/api/module/formManggeApi";
import { fabric, cache } from "fabric";
import draggable from "vuedraggable";
import pdf from "vue-pdf";
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
const SIGN = "SIGN"; // 签字
const STAMP = "STAMP"; // 印章
export default {
name: "drag-sign-seal",
props: {
entity: {
type: Object,
default: () => {
return {};
},
},
isPortrait: {
type: Boolean,
default: true
},
sealParameter: {
type: Object,
default: () => {
return {};
},
},
fileObj: {
type: Object,
default: () => {
return {};
},
},
sealSignPositionFieldMap: {
type: Object,
default: () => {
return {};
},
},
isOwnSign: {
type: Boolean,
default: false,
},
addr: {
type: String,
default: '',
},
pageN: {
type: Number,
default: 1,
},
isScroll: {
type: Boolean,
default: false,
}
},
components: {
draggable,
},
data() {
return {
touchStartHandler: null,
touchMoveHandler: null,
fileUrl: '',
menuTree: [
{ name: "电子印章", children: [], pid: null, isOwnSign: false },
{
name: "本人签名",
pid: null,
children: [],
isOwnSign: false
},
],
elTreeProps: {
children: "children",
label: "name",
},
showLoadingChild: false,
signs: {},
//pdf预览
pdfUrl: "",
pdfDoc: null,
numPages: 1,
pageNum: 1,
scale: 2,
currentScale: 1,
minScale: 0.5, // 最小缩放比例
maxScale: 5.0, // 最大缩放比例
scaleStep: 0.1, // 缩放步长
pageRendering: false,
pageNumPending: null,
canvas: null,
ctx: null,
canvasEle: null,
whDatas: null,
mainImagelist: [],
signList: [],
sealList: [],
sealMap: {},
activeName: "",
currItemIndex: {},
dateList: [],
fileType: "",
previewFile: null,
mimeType:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
currentPage: 1,
mobileScale: 0.2,
objectMovingFlag:false,
};
},
computed: {
userInfo() {
return this.$store.state.user;
},
},
watch: {
fileObj: {
handler() {
if (!this.fileObj || !this.fileObj?.fileId) {
return;
}
this.init();
},
deep: true,
immediate: true,
},
whDatas: {
handler() {
// this.showLoading = true;
if (this.whDatas) {
// this.showLoading = false;
this.renderFabric();
this.canvasEvents();
}
},
immediate: false,
deep: true,
},
pageNum: {
handler(newVal, oldVal) {
this.commonSign(this.pageNum);
this.queueRenderPage(this.pageNum);
this.currentPage = newVal;
},
deep: true,
},
sealSignPositionFieldMap: {
handler(n) {
if (n && !n.length) {
this.canvasEle.remove(this.canvasEle.clear())
}
},
deep: true
}
},
created() {},
mounted() {
if (!this.isScroll) {
this.initScroll()
}
this.enableScroll();
},
methods: {
enableScroll() {
let _this = this;
const container = this.$refs.canvasDivBox;
let isDown = false;
let startX, startY;
let scrollLeft, scrollTop;
// 鼠标事件
container.addEventListener('mousedown', (e) => {
if(_this.objectMovingFlag){
return;
}
isDown = true;
startX = e.pageX - container.offsetLeft;
startY = e.pageY - container.offsetTop;
scrollLeft = container.scrollLeft;
scrollTop = container.scrollTop;
});
container.addEventListener('mouseleave', () => {
if(_this.objectMovingFlag){
return;
}
isDown = false;
});
container.addEventListener('mouseup', () => {
if(_this.objectMovingFlag){
return;
}
isDown = false;
});
container.addEventListener('mousemove', (e) => {
if(_this.objectMovingFlag){
return;
}
if (!isDown) return;
e.preventDefault();
const x = e.pageX - container.offsetLeft;
const y = e.pageY - container.offsetTop;
const walkX = (x - startX) * 2; // 水平滚动速度
const walkY = (y - startY) * 2; // 垂直滚动速度
container.scrollLeft = scrollLeft - walkX;
container.scrollTop = scrollTop - walkY;
});
// 触摸事件支持
container.addEventListener('touchstart', (e) => {
if(_this.objectMovingFlag){
return;
}
isDown = true;
startX = e.touches[0].pageX - container.offsetLeft;
startY = e.touches[0].pageY - container.offsetTop;
scrollLeft = container.scrollLeft;
scrollTop = container.scrollTop;
});
container.addEventListener('touchend', () => {
if(_this.objectMovingFlag){
return;
}
isDown = false;
});
container.addEventListener('touchmove', (e) => {
if(_this.objectMovingFlag){
return;
}
if (!isDown) return;
const x = e.touches[0].pageX - container.offsetLeft;
const y = e.touches[0].pageY - container.offsetTop;
const walkX = (x - startX) * 2;
const walkY = (y - startY) * 2;
container.scrollLeft = scrollLeft - walkX;
container.scrollTop = scrollTop - walkY;
});
},
initScroll() {
const scrollableDiv = document.getElementById('col-left');
const scrollableDiv1 = document.getElementById('app');
let startY1 = 0;
let scrollTop1 = 0;
// 定义 touchstart 和 touchmove 处理函数
let startY = 0;
let scrollTop = 0;
this.touchStartHandler = (e) => {
startY = e.touches[0].clientY;
scrollTop = scrollableDiv.scrollTop;
startY1 = e.touches[0].clientY;
scrollTop1 = scrollableDiv1.scrollTop;
};
this.touchMoveHandler = (e) => {
e.preventDefault(); // 阻止默认滚动行为
const currentY = e.touches[0].clientY;
const diff = currentY - startY;
scrollableDiv.scrollTop = scrollTop - diff;
const currentY1 = e.touches[0].clientY;
const diff1 = currentY1 - startY1;
scrollableDiv1.scrollTop = scrollTop1 - diff1;
};
// 添加事件监听
document.addEventListener('touchstart', this.touchStartHandler);
document.addEventListener('touchmove', this.touchMoveHandler, { passive: false });
},
removeScrollListeners() {
// 移除事件监听
if (this.touchStartHandler) {
document.removeEventListener('touchstart', this.touchStartHandler);
}
if (this.touchMoveHandler) {
document.removeEventListener('touchmove', this.touchMoveHandler);
}
},
async init() {
await this.getFileServerConfig();
this.initProperties();
// this.findSignByUserId();
// this.findCanUseSealList();
this.setPdfArea();
// 监听键盘事件
document.addEventListener("keydown", (event) => {
// 检查按下的是否是删除键(Delete键或Backspace键)
if (event.key === "Delete" || event.key === "Backspace") {
const activeObject = this.canvasEle.getActiveObject();
if (activeObject) {
// 删除活动对象
this.canvasEle.remove(activeObject);
// 清空活动对象
this.canvasEle.setActiveObject(null);
// 渲染画布以显示更改
this.canvasEle.renderAll();
}
}
});
// 我的表单新增时,只有签名
if (this.isOwnSign) {
this.menuTree[0].isOwnSign = true;
}
// 执行页码
this.pageNumber();
},
pageNumber() {
this.currentPage = this.pageN;
this.cutover();
},
/**
* @description: 设置PDF预览区域内容
* @return {*}
*/
async setPdfArea() {
let pdfUrl = "";
if (!this.fileObj.fileId || this.fileObj.fileId === '') {
this.$toast("没有需处理的文件");
return;
}
if (this.isPdf()) {
const pdfFile = this.$STDCOMMON.fileOperation.base64ToFile(
this.fileObj.content,
this.fileName,
this.mimeType
);
pdfUrl = URL.createObjectURL(pdfFile);
} else {
this.showLoadingChild = true;
await this.getFile();
this.showLoadingChild = false;
if (!this.previewFile) {
this.pageTotal = 0;
return;
}
pdfUrl = URL.createObjectURL(this.previewFile);
}
this.$nextTick(() => {
this.showpdf(pdfUrl);
});
},
/**
* @description: 渲染pdf,到时还会盖章信息,在渲染时,同时显示出来,不应该在切换页码时才显示印章信息
* @return {*}
*/
showpdf(pdfUrl) {
this.canvas = document.getElementById("the-canvas");
this.ctx = this.canvas.getContext("2d");
this.pdfUrl = pdf.createLoadingTask({
url: pdfUrl,
// 解决pdf文件,后台填充的文本不显示问题,与字体有关
CMapReaderFactory,
});
this.pdfUrl.promise
.then((pdfDoc_) => {
this.pdfDoc = pdfDoc_;
this.numPages = this.pdfDoc.numPages;
this.renderPage(this.pageNum).then(() => {
const pdfContainer = document.getElementById('col-left');
this.renderPdf({
width: pdfContainer.clientWidth,
height: pdfContainer.clientHeight,
});
this.commonSign(this.pageNum, true);
});
})
.catch((err) => {
this.pageTotal = 0;
// this.showLoading = false;
});
},
// 放大
zoomIn() {
this.currentScale = Math.min(this.currentScale + this.scaleStep, this.maxScale);
this.applyZoom();
},
// 缩小
zoomOut() {
this.currentScale = Math.max(this.currentScale - this.scaleStep, this.minScale);
this.applyZoom();
},
// 应用缩放
async applyZoom() {
document.getElementById('canvas-div').style.transform = `scale(${this.currentScale})`;
},
/**
* @description: 初始化当前页的PDF内容,canvas宽度高度
* @param {*} num 当前页
* @return {*}
*/
renderPage(num) {
let _this = this;
this.pageRendering = true;
return this.pdfDoc.getPage(num).then((page) => {
// 获取目标 div 的宽度
const pdfContainer = document.getElementById('col-left');
const containerWidth = pdfContainer.clientWidth;
// 获取页面视图的原始尺寸
const originalViewport = page.getViewport({ scale: 1 });
// 计算适应 div 宽度的缩放比例
const margin = 10; // 边距设置
this.mobileScale = (containerWidth) / originalViewport.width;
// 重新获取视口,根据计算的 scale
let viewport = page.getViewport({ scale: this.scale }); //设置视口大小 scale数值越大,清晰度越高,占用内存越高
_this.canvas.height = viewport.height;
_this.canvas.width = viewport.width;
_this.canvasEle && _this.canvasEle.setHeight(viewport.height);
_this.canvasEle && _this.canvasEle.setWidth(viewport.width);
let renderContext = {
canvasContext: _this.ctx,
viewport: viewport,
};
let renderTask = page.render(renderContext);
renderTask.promise.then(() => {
_this.pageRendering = false;
if (_this.pageNumPending !== null) {
this.renderPage(_this.pageNumPending);
_this.pageNumPending = null;
}
});
});
},
/**
* @description: 设置绘图区域宽高
* @return {*}
*/
renderPdf(data) {
this.whDatas = data;
},
queueRenderPage(num) {
if (this.pageRendering) {
this.pageNumPending = num;
} else {
this.renderPage(num);
}
},
/**
* @description: 翻页展示盖章信息
* @param {*} pageNum 第几页
* @param {*} isFirst 是否第一页
* @return {*}
*/
commonSign(pageNum, isFirst = false) {
if (isFirst == false) this.canvasEle.remove(this.canvasEle.clear()); //清空页面所有签章
if (!this.signs || !Object.keys(this.signs).length) return false;
let datas = this.signs[pageNum];
if (datas != null && datas != undefined) {
for (let index in datas) {
const obj = datas[index];
const params = {
sealUrl: obj.type === "i-text" ? obj.text : obj.sealUrl,
left: obj.left ,
top: obj.top ,
index: obj.index,
scaleX: obj.scaleX,
scaleY: obj.scaleY,
type: obj.type,
useId: obj.useId,
fileId: obj.fileId,
hasControls: obj.type === SIGN,
angle:obj.angle || 0,
selectable: obj?.selectable,
creatorId: obj?.creatorId,
creatorName: obj?.creatorName,
createTime: obj?.createTime,
writeSignBase64: obj?.writeSignBase64
};
this.addSeal(params);
}
}
},
/**
* @description: 生成绘图区域,并设置绘图区域的宽高,位置与预览的canvas重叠
* @return {*}
*/
renderFabric() {
let canvaEle = document.querySelector("#ele-canvas");
const thecanvas = document.querySelector("#the-canvas");
canvaEle.width = document.querySelector("#the-canvas").clientWidth;
canvaEle.height = document.querySelector("#the-canvas").clientHeight;
this.canvasEle = new fabric.Canvas(canvaEle);
let container = document.querySelector(".canvas-container");
container.style.position = "absolute";
container.style.top = "3px";
// container.style.left = (width - document.querySelector("#the-canvas").clientWidth) / 2 - 3 + "px";
this.pageNumber();
},
/**
* @description: 相关事件操作,比如在绘图区域点击事件,元素的拖拽事件
* @return {*}
*/
canvasEvents() {
let _this = this;
// 鼠标在绘图区域的点击事件,鼠标按下时,如果在绘制的对象上,则使对象进入编辑状态
this.canvasEle.on("mouse:down", (options) => {
var activeObject = options.target;
if (activeObject && activeObject?.selectable) {
_this.objectMovingFlag = true;
}
if (activeObject && activeObject.isEditing) {
return;
}
if (activeObject && activeObject.type === "textbox") {
activeObject.enterEditing();
activeObject.selectAll();
}
this.canvasEle.renderAll();
});
// 拖拽边界 不能将图片拖拽到绘图区域外
this.canvasEle.on("object:moving", function (e) {
_this.objectMovingFlag = true;
var obj = e.target;
// if object is too big ignore
if (
obj.currentHeight > obj.canvas.height ||
obj.currentWidth > obj.canvas.width
) {
return;
}
obj.setCoords();
// top-left corner
if (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 corner
if (
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
);
}
});
// 监听所有对象的拖动结束
this.canvasEle.on('object:modified', function(e) {
_this.objectMovingFlag = false ;
});
},
/**
* @description: 拖拽时添加fabric对象,签名,印章,文本等
* @param {*} sealUrl 如果是图片,则是图片的url,如果是文本,则是文本内容
* @param {*} left 左间距
* @param {*} top 顶部间距
* @param {*} index 索引:mainImagelist的
* @param {*} scaleX x轴比例尺
* @param {*} scaleY y轴比例尺
* @param {*} type 类型:TEXT,SIGN,STAMP
* @param {*} useId 签名id,或者印章id
* @param {*} fileId 签名或印章的文件id
* @param {*} hasControls 是否支持编辑
* @param {*} isInit
* @return {*} sealUrl, left, top, index, scaleX, scaleY, type, useId, fileId, hasControls = true, isInit = false
*/
addSeal(params) {
// 缩放比例
const scaling = this.mobileScale / 2;
const scrollableDiv = document.getElementById('canvas-div');
const scrollTop = scrollableDiv.scrollTop;
// 签名和印章使用图片元素
fabric.Image.fromURL(params.sealUrl, (oImg) => {
oImg.hasControls = params.hasControls;
oImg.set({
left: params.left * scaling,
top: (params.top + scrollTop) * scaling,
scaleX: params.scaleX || 1,
scaleY: params.scaleY || 1,
index: params.index,
hasControls: params.hasControls,
type: params.type,
useId: params.useId,
fileId: params.fileId,
sealUrl:params.fileId ? `${this.fileUrl}/dl/view/${params.fileId}`:params?.writeSignBase64,
pageNum: this.pageNum,
businessId: this.entity.id,
sealSignId: params.sealSignId,
pageWidth: this.canvas.width,
pageHeight: this.canvas.height,
newFieldFlag: params.newFieldFlag,
selectable: params.selectable,
creatorId: params?.creatorId,
creatorName: params?.creatorName,
createTime: params?.createTime,
writeSignBase64: params?.writeSignBase64
});
oImg.lockRotation = true; // 禁止旋转
// 签名禁止左右上下拖动,只支持按比例拖动
oImg.setControlVisible("mt", false);
oImg.setControlVisible("mb", false);
oImg.setControlVisible("ml", false);
oImg.setControlVisible("mr", false);
oImg.set({ angle: params.angle });
// oImg.scale(0.5); //图片缩小一
if(params.type === STAMP){
oImg.scale(0.38);
} else {
oImg.scale(scaling * params.scaleX);
}
this.$nextTick(()=>{
this.canvasEle && this.canvasEle.add(oImg);
})
});
},
getSealSignPositionFieldList() {
const data = this.confirmSignature();
const result = [];
for (const key in data) {
const cache = data[key];
if (JSON.stringify(cache) === "{}") {
continue;
}
for (const k in cache) {
const ele = cache[k];
if (JSON.stringify(ele) === "{}") {
continue;
}
result.push(ele);
}
}
if (result && result.length) {
const scaling = this.mobileScale / 2;
result.forEach(i => {
// if (i.type === SIGN) {
this.$set(i, 'scaleX', i.scaleX / scaling);
this.$set(i, 'scaleY', i.scaleY / scaling);
// this.$set(i, 'pageHeight', i.pageHeight / scaling);
this.$set(i, 'bly', i.bly / scaling);
// }
})
}
return result;
},
/**
* @description: 判断是否是pdf文件
* @return {*}
*/
isPdf() {
if (this.fileObj?.fileName) {
const arr = this.fileObj?.fileName.split(".");
this.fileType = arr[arr.length - 1];
}
return this.fileType && this.fileType.toLowerCase() === "pdf";
},
async getFile() {
return await getPdfFile({
id: this.fileObj?.id,
fileId: this.fileObj?.fileId,
fileName: this.fileObj?.fileName,
type: this.fileType,
content: this.fileObj?.content,
})
.then((res) => {
if (!res.success) {
this.previewFile = null;
return;
}
if (!res.data) {
this.previewFile = null;
return;
}
this.previewFile = this.$STDCOMMON.fileOperation.base64ToFile(
res.data,
this.fileName,
this.mimeType
);
})
.catch(() => {
this.previewFile = null;
});
},
/**
* @description: 上一页点击事件:确认签章位置等属性并保存到缓存
* @return {*}
*/
prevPage() {
this.confirmSignature();
if (this.pageNum <= 1) {
return;
}
this.pageNum--;
this.currentPage = this.pageNum;
this.$emit('page', this.currentPage);
},
/**
* @description: 下一页点击事件:确认签章位置等属性并保存到缓存
* @return {*}
*/
nextPage() {
this.confirmSignature();
if (this.pageNum >= this.numPages) {
return;
}
this.pageNum++;
this.currentPage = this.pageNum;
this.$emit('page', this.currentPage);
},
/**
* @description: 跳转按钮事件:确认签章位置等属性并保存到缓存
* @return {*}
*/
cutover() {
this.confirmSignature();
this.pageNum = this.currentPage;
if (!this.currentPage) {
this.pageNum = 1;
this.currentPage = 1;
}
this.$emit('page', this.currentPage);
},
/**
* @description: 确认签章位置并保存到缓存
* @return {*}
*/
confirmSignature() {
let data = this.canvasEle.getObjects(); //获取当前页面内的所有签章信息
let caches = JSON.parse(JSON.stringify(this.signs)); //获取缓存字符串后转换为对象
let signDatas = {}; //存储当前页的所有签章信息
let i = 0;
for (var val of data) {
signDatas[i] = {
width: val.width, // 签字,盖章,文本元素的宽度和高度
height: val.height,
top: val.top / (this.mobileScale / 2), // 据顶部距离
left: val.left / (this.mobileScale / 2), // 距左侧距离
angle: val.angle,
translateX: val.translateX,
translateY: val.translateY,
scaleX: val.scaleX, // 比例尺x
scaleY: val.scaleY, // 比例尺y
pageNum: val.pageNum, // pdf第几页
sealUrl: val.sealUrl, // 签字或印章文件的地址
useId: val.useId, // 签字或印章数据的id
fileId: val.fileId, // 签字或印章的文件id
index: val.index, // 索引,目前用不到
type: val.type, // 元素类型:i-text, image
type: val.type, // 类型,签字,盖章,文本
businessId: val.businessId, // 表单id
sealSignId: val.sealSignId, // 印章/签名对应的业务id
hasControls: val.hasControls, // 是否可编辑,拖动操作
text: val.text, // 如果是文本:文本内容
pageWidth: val.pageWidth, // 页面宽度
pageHeight: val.pageHeight, // 页面高度
tlx: val.aCoords.tl.x, //左上角x
tly: val.aCoords.tl.y, // 左上角y
trx: val.aCoords.tr.x, // 右上角x
try: val.aCoords.tr.y, // 右上角y
blx: val.aCoords.bl.x + 3, // 左下角x
bly: val.aCoords.bl.y, // 左下角y
brx: val.aCoords.br.x, // 右下角x
bry: val.aCoords.br.y, // 右下角 y
newFieldFlag: val?.newFieldFlag,
scale:this.scale,
selectable: val.selectable, // 是否可拖动
creatorId: val?.creatorId,
creatorName: val?.creatorName,
createTime: val?.createTime,
writeSignBase64: val?.writeSignBase64
};
i++;
}
if (caches == null) {
caches = {};
caches[this.pageNum] = signDatas;
} else {
caches[this.pageNum] = signDatas;
}
this.signs = caches;
return this.signs;
},
initProperties() {
this.pageNum = 1;
this.numPages = 1;
this.pdfUrl = "";
this.whDatas = null;
// 处理默认数据,只能推拽自己的盖章和签字
let sealSignPositionFieldMap = this.sealSignPositionFieldMap;
if (sealSignPositionFieldMap) {
Object.keys(sealSignPositionFieldMap).forEach((key) => {
let list = sealSignPositionFieldMap[key];
list.forEach((item) => {
item.selectable = item.creatorId === this.userInfo.id;
if(!item.fileId && item?.writeSignBase64){
item.sealUrl = item?.writeSignBase64;
}
});
});
}
this.signs = sealSignPositionFieldMap;
},
removeSignature() {
this.canvasEle.remove(this.canvasEle.getActiveObject());
},
/**
* @description: 拖动结束,判断是否在指定的区域,如果在预览区域内,则将印章放进去,否则不绘制
* @param {*} e
* @return {*}
*/
end(e, obj) {
const canvasDiv = this.$refs.canvasDiv; // 指定区域的DOM元素
const rect = canvasDiv.getBoundingClientRect();
const scrollableDiv = document.getElementById('canvas-div');
const scrollTop = scrollableDiv.scrollTop;
const scrollLeft = scrollableDiv.scrollLeft;
if (
e.originalEvent.changedTouches[0].pageX >= rect.left &&
e.originalEvent.changedTouches[0].pageX <= rect.right &&
e.originalEvent.changedTouches[0].pageY >= rect.top &&
e.originalEvent.changedTouches[0].pageY <= rect.bottom
) {
// end元素中取originalEvent 里边有4组xy,多试一下该用哪个
const type = obj.type;
let params = {
sealUrl: obj.url,
left: e.originalEvent.changedTouches[0].clientX + scrollLeft,
top: e.originalEvent.changedTouches[0].clientY,
index: e.newDraggableIndex,
scaleX: 1,
scaleY: 1,
type: type,
useId: obj.useId,
fileId: obj.fileId,
sealSignId: obj.sealSignId,
hasControls: type === SIGN,
isInit: true,
newFieldFlag: "1",
angle:obj.angle || 0,
selectable: true, // false禁止选择,从而禁止拖动
};
this.addSeal(params);
} else {
this.$toast("请将个人签名或电子印章拖入指定区域内");
}
},
/**
* @description: 查询当前登录人的签名信息,并处理成需要的格式
* @return {*}
*/
findSignByUserId() {
return findSignListByUserId(this.userInfo.id)
.then((res) => {
if (!res.success || !res.data || !res.data.length) {
return;
}
res.data = res.data.filter(item => {
return item.fileId != null && item.fileId !== '';
});
if (!res.success || !res.data || !res.data.length) {
return;
}
let list = [];
res.data.forEach((item) => {
list.push({
url: item.content,
useId: this.userInfo.id,
fileId: item.fileId,
type: SIGN,
pid: SIGN,
sealSignId: item.businessId,
hasControls: true,
});
});
this.menuTree[1].children = list;
this.mainImagelist.push(...list);
})
.catch((err) => {
this.$message.error(err);
});
},
findCanUseSealList() {
findCanUseSealList({
userId: this.userInfo.id,
moduleCode: 'smartsFormCenterAudit',
})
.then((res) => {
if (!res.success || !res.data) {
return;
}
let list = [];
res.data.forEach((item) => {
list.push({
url: item.file.content,
useId: this.userInfo.id,
fileId: item.file.fileId,
type: STAMP,
pid: STAMP,
sealSignId: item.id,
hasControls: false,
sealName: item.sealName,
});
});
if (!this.isOwnSign) {
this.menuTree[0].children = list;
this.mainImagelist.push(...list);
}
})
.catch(() => {
// this.showLoading = false;
});
},
getFileServerConfig() {
return getFileServerConfig()
.then((res) => {
if (!res.success || !res.data) {
return;
}
this.fileUrl = res.data.fileServerUrl;
})
.catch(() => {
// this.showLoading = false;
});
},
nullity() {
if (!this.currentPage) {
this.pageNum = 1;
this.currentPage = 1;
}
},
},
beforeDestroy() {
this.removeScrollListeners();
},
};
</script>
<style lang="scss" >
.drag-sign-seal {
.el-tree {
overflow: auto;
}
.el-row {
border: 0px solid rgba(190, 197, 208, 1);
border-radius: 2px;
}
.col-left {
flex-direction: column;
text-align: center;
.el-input__inner {
width: 100% !important;
}
}
.col-right {
display: none !important;
border-left: 1px solid rgba(190, 197, 208, 1);
height: 100%;
padding: 15px;
.el-tree {
margin-top: 10px;
height: calc(100% - 40px);
}
.custom-tree-node {
height: 26px !important;
}
.el-tree-node__content {
height: auto;
padding-left: 0 !important;
}
}
/*pdf部分*/
.pCenter {
display: block;
height: 100%;
// overflow: scroll;
//overflow-x: auto;
//overflow-y: auto;
// &::-webkit-scrollbar {
// width: 0.12rem !important;
// height: 0.12rem !important;
// }
// &::-webkit-scrollbar-thumb {
// border-radius: 6px;
// background-color: #dedede;
// }
// &::-webkit-scrollbar-track {
// border-radius: 6px;
// background-color: rgba(0, 0, 0, 0);
// }
}
#the-canvas {
// margin-top: 10px;
border: 0px solid #f0f0f0;
}
#ele-canvas {
border: 0px solid #f0f0f0;
}
html:fullscreen {
background: white;
}
.elesign {
display: flex;
flex: 1;
flex-direction: column;
position: relative;
margin: auto;
height: 100%;
}
.page {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
margin: 0 auto;
margin-top: 1%;
margin-bottom: 1%;
display: flex;
.el-button--small span {
color: #444 !important;
}
.el-input__inner {
width: 0.6rem;
text-align: center;
}
}
.canvas-div-box {
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: auto;
-webkit-overflow-scrolling: touch; /* 启用iOS惯性滚动 */
scrollbar-width: none; /* 隐藏Firefox滚动条 */
// &::-webkit-scrollbar {
// -webkit-appearance: none;
// width: 8px; /* 垂直滚动条宽度 */
// height: 8px; /* 水平滚动条高度 */
// }
// &::-webkit-scrollbar-thumb {
// background-color: rgba(0, 0, 0, 0.3);
// border-radius: 4px;
// }
// &::-webkit-scrollbar-track {
// background-color: rgba(0, 0, 0, 0.05);
// }
}
.canvas-div {
width: 100%;
margin: 0;
overflow-y: hidden;
overflow-x: hidden;
// height: 100%;
position: relative;
text-align: left;
float: left;
transform-origin: 0 0;
}
#col-left-box {
height: 94%;
}
@keyframes ani-demo-spin {
from {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
to {
transform: rotate(360deg);
}
}
li {
list-style-type: none;
padding: 10px;
}
.imgstyle {
vertical-align: middle;
width: 130px;
// border: solid 1px #e8eef2;
// background-image: url("./img/rank.png");
background-repeat: no-repeat;
}
.btn-outline-default {
color: #444;
background-color: transparent;
background-image: none;
border: 1px solid #dcdfe6;
margin-left: 0.1rem;
}
.btn-outline-default:hover {
color: #444;
// border-color: #009994;
background-color: #ecfbf8;
}
.draggable-list {
height: calc(100% - 50px);
margin-bottom: 5px;
&::-webkit-scrollbar {
width: 0px;
height: 6px;
}
}
.lock {
div.lock-icon {
position: absolute;
bottom: 0px;
right: 0px;
vertical-align: middle;
width: 20px;
height: 20px;
// background-image: url("./img/lock.png");
background-repeat: no-repeat;
cursor: pointer;
}
img {
cursor: not-allowed;
background-color: rgba(232, 238, 242, 0.7); /* 半透明遮罩 */
z-index: 10;
}
}
.item {
position: relative;
}
.unlock {
& > span {
&:hover {
background-color: rgba(0, 153, 148, 0.05);
cursor: move;
}
}
}
.nodrag-tag {
cursor: pointer;
}
.date-item {
text-align: center;
width: 160px;
height: 50px;
line-height: 50px;
background-color: transparent;
&:hover {
cursor: move;
}
}
}
.landscape .van-tree-select__nav {
height: 2rem;
}
.landscape .van-tree-select__content {
height: 2rem;
}
.landscape .canvas-div {
height: 11rem!important;
margin: 0!important;
}
.landscape .col-left-box {
padding-right: 0.4rem;
overflow: auto;
height: 2rem;
}
.landscape .page {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
margin-top: -0.01rem;
/* margin-top: 0.5%; */
margin-bottom: 0.5%;
display: flex;
font-size: 0.16rem;
position: fixed;
/* margin-top: 3rem; */
/* top: 3rem; */
z-index: 3;
background: white;
width: 100%;
/* height: 0.2rem; */
/* top: 0.5rem;*/
.el-button--small span {
color: #444 !important;
}
}
</style>
这是个移动端的签字盖章的组件,现在有个问题,当我翻页的时候,上一页的签名会缩小
最新发布