@ -0,0 +1,16 @@ | |||||
/* components/canvas-drag/index.wxss */ | |||||
.movable-label { | |||||
margin-top: 300rpx; | |||||
width: 750rpx; | |||||
height: 400rpx; | |||||
background: #eee; | |||||
} | |||||
.movable-block { | |||||
width: 120rpx; | |||||
height: 120rpx; | |||||
background: #ccc; | |||||
} | |||||
.movable-block .image-con { | |||||
width: 100%; | |||||
height: 100%; | |||||
} |
@ -0,0 +1,63 @@ | |||||
export default { | |||||
data() { | |||||
return { | |||||
} | |||||
}, | |||||
methods: { | |||||
// 设置数据 | |||||
setData: function(obj, callback) { | |||||
let that = this; | |||||
const handleData = (tepData, tepKey, afterKey) => { | |||||
tepKey = tepKey.split('.'); | |||||
tepKey.forEach(item => { | |||||
if (tepData[item] === null || tepData[item] === undefined) { | |||||
let reg = /^[0-9]+$/; | |||||
tepData[item] = reg.test(afterKey) ? [] : {}; | |||||
tepData = tepData[item]; | |||||
} else { | |||||
tepData = tepData[item]; | |||||
} | |||||
}); | |||||
return tepData; | |||||
}; | |||||
const isFn = function(value) { | |||||
return typeof value == 'function' || false; | |||||
}; | |||||
Object.keys(obj).forEach(function(key) { | |||||
let val = obj[key]; | |||||
key = key.replace(/\]/g, '').replace(/\[/g, '.'); | |||||
let front, after; | |||||
let index_after = key.lastIndexOf('.'); | |||||
if (index_after != -1) { | |||||
after = key.slice(index_after + 1); | |||||
front = handleData(that, key.slice(0, index_after), after); | |||||
} else { | |||||
after = key; | |||||
front = that; | |||||
} | |||||
if (front.$data && front.$data[after] === undefined) { | |||||
Object.defineProperty(front, after, { | |||||
get() { | |||||
return front.$data[after]; | |||||
}, | |||||
set(newValue) { | |||||
front.$data[after] = newValue; | |||||
that.$forceUpdate(); | |||||
}, | |||||
enumerable: true, | |||||
configurable: true | |||||
}); | |||||
front[after] = val; | |||||
} else { | |||||
that.$set(front, after, val); | |||||
} | |||||
}); | |||||
// this.$forceUpdate(); | |||||
isFn(callback) && this.$nextTick(callback); | |||||
}, | |||||
} | |||||
} |
@ -0,0 +1,195 @@ | |||||
<template> | |||||
<view class="page"> | |||||
<navbar title="电子合同" | |||||
leftClick | |||||
@leftClick="$utils.navigateBack"/> | |||||
<view style="padding: 25rpx;"> | |||||
<canvas-drag ref="canvasRef" | |||||
id="canvas-drag" | |||||
:graph="graph" | |||||
:width="width" | |||||
:height="height" | |||||
bgColor="#fff" | |||||
enableUndo="true"></canvas-drag> | |||||
<!-- <canvas | |||||
canvas-id="mycanvas" | |||||
class="mycanvas" | |||||
v-if="!imagePath" | |||||
:style="{height : height + 'rpx', width : width + 'rpx'}" | |||||
disable-scroll="true"></canvas> --> | |||||
<image :src="imagePath" | |||||
v-if="imagePath" | |||||
style="width: 700rpx;" | |||||
mode="widthFix"></image> | |||||
</view> | |||||
<view class="uni-color-btn" | |||||
@click.stop="onChangeBgImage"> | |||||
导入合同 | |||||
</view> | |||||
<view class="uni-color-btn" | |||||
@click.stop="toSignature"> | |||||
签名 | |||||
</view> | |||||
<view class="uni-color-btn" | |||||
@click.stop="onExportJSON"> | |||||
保存合同 | |||||
</view> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
import canvasDrag from "@/components/canvas-drag/index"; | |||||
import setData from "@/components/canvas-drag/setData.js"; | |||||
export default { | |||||
components: { | |||||
canvasDrag | |||||
}, | |||||
mixins : [setData], | |||||
data() { | |||||
return { | |||||
id : 0, | |||||
detail : {}, | |||||
height: 750, | |||||
width: 700, | |||||
ctx : null, | |||||
imagePath : 0, | |||||
} | |||||
}, | |||||
onLoad({id}) { | |||||
this.id = id | |||||
this.getDetail() | |||||
}, | |||||
onShow() { | |||||
this.initCanvas() | |||||
this.syntheticSignature() | |||||
}, | |||||
methods: { | |||||
// 获取合同内容 | |||||
getDetail(){ | |||||
let data = { | |||||
contractId: this.id | |||||
} | |||||
if (uni.getStorageSync('token')) { | |||||
// data.token = uni.getStorageSync('token') | |||||
} | |||||
this.$api('queryContracById', data, res => { | |||||
if (res.code == 200) { | |||||
this.detail = res.result | |||||
} | |||||
}) | |||||
}, | |||||
initCanvas() { | |||||
this.ctx = uni.createCanvasContext("mycanvas") | |||||
}, | |||||
// 去签名 | |||||
toSignature(){ | |||||
uni.navigateTo({ | |||||
url: `/pages_order/contract/electronicSignature?id=${this.id}&signature=1` | |||||
}) | |||||
}, | |||||
// 导出合同 | |||||
onExportJSON() { | |||||
uni.showLoading({ | |||||
title: '保存中...' | |||||
}) | |||||
this.$refs.canvasRef.exportFun().then(image => { | |||||
console.log(image); | |||||
uni.hideLoading() | |||||
// this.imagePath = image | |||||
uni.previewImage({ | |||||
urls: [image], | |||||
current: 0, | |||||
}) | |||||
}).catch(e => { | |||||
uni.hideLoading() | |||||
console.error(e); | |||||
}); | |||||
}, | |||||
/** | |||||
* 导入合同 | |||||
*/ | |||||
onChangeBgImage() { | |||||
let self = this | |||||
wx.chooseImage({ | |||||
success: res => { | |||||
uni.getImageInfo({ | |||||
src: res.tempFilePaths[0], | |||||
success: image => { | |||||
let allwh = image.height + image.width | |||||
let imgw = (image.width / allwh).toFixed(2) | |||||
let imgh = (image.height / allwh).toFixed(2) | |||||
self.height = imgh * Math.ceil(this.width / imgw) | |||||
// this.width = image.width | |||||
// this.height = image.height | |||||
self.$nextTick(() => { | |||||
this.$refs.canvasRef.changeBgImage(image.path) | |||||
// this.ctx.drawImage(image.path, 0, 0, this.ctx.width, this.ctx.height) | |||||
// this.ctx.stroke(); | |||||
// this.ctx.draw(false, () => { | |||||
// wx.canvasToTempFilePath({ | |||||
// canvasId: 'mycanvas', | |||||
// success: res => { | |||||
// // this.imagePath = res.tempFilePath | |||||
// uni.previewImage({ | |||||
// urls: [res.tempFilePath], | |||||
// current: 0, | |||||
// }) | |||||
// }, | |||||
// fail: e => { | |||||
// } | |||||
// }, this); | |||||
// }); | |||||
}) | |||||
} | |||||
}) | |||||
} | |||||
}); | |||||
}, | |||||
// 合成签名 | |||||
syntheticSignature(){ | |||||
let url = uni.getStorageSync('electronicSignature') | |||||
if(!url) return | |||||
uni.removeStorageSync('electronicSignature') | |||||
this.setData({ | |||||
graph: { | |||||
w: 100, | |||||
h: 50, | |||||
x : 0, | |||||
y : 0, | |||||
type: 'image', | |||||
url, | |||||
permitSelected : true, | |||||
} | |||||
}); | |||||
// this.$nextTick(() => { | |||||
// this.onExportJSON() | |||||
// }) | |||||
}, | |||||
} | |||||
} | |||||
</script> | |||||
<style scoped lang="scss"> | |||||
.page{ | |||||
.mycanvas{ | |||||
background-color: #fff; | |||||
} | |||||
} | |||||
</style> |
@ -0,0 +1,88 @@ | |||||
<template> | |||||
<view class="page"> | |||||
<navbar title="合同模板" | |||||
leftClick | |||||
@leftClick="$utils.navigateBack"/> | |||||
<view class="content"> | |||||
<view class="projectContent" | |||||
@click="$utils.navigateTo('/pages_order/contract/contractManageEdit')" | |||||
> | |||||
<image src="../static/contract/contract.png" alt="" /> | |||||
<view class="info"> | |||||
<view class="projectName"> | |||||
xxxx电子合同 | |||||
</view> | |||||
<view class="text"> | |||||
甲方:湖南瀚海科技有限公司 | |||||
</view> | |||||
<view class="text"> | |||||
乙方:四川特能博世科技有限公司 | |||||
</view> | |||||
</view> | |||||
<view class="run" | |||||
@click.stop="$utils.navigateTo('/pages_order/contract/contractManageEdit')"> | |||||
<uv-icon | |||||
name="arrow-right" | |||||
color="#2979ff" | |||||
size="30rpx"></uv-icon> | |||||
</view> | |||||
</view> | |||||
</view> | |||||
<image src="/static/image/1.png" mode=""></image> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
data() { | |||||
return { | |||||
} | |||||
}, | |||||
methods: { | |||||
} | |||||
} | |||||
</script> | |||||
<style scoped lang="scss"> | |||||
.page{ | |||||
.content { | |||||
width: 100%; | |||||
height: 100%; | |||||
.projectContent { | |||||
background-color: #fff; | |||||
display: flex; | |||||
margin: 30rpx; | |||||
border-radius: 20rpx; | |||||
image { | |||||
width: 140rpx; | |||||
height: 120rpx; | |||||
margin: 20rpx; | |||||
} | |||||
.info { | |||||
margin: 28rpx 10rpx; | |||||
.projectName { | |||||
font-size: 32rpx; | |||||
} | |||||
.text { | |||||
font-size: 24rpx; | |||||
} | |||||
} | |||||
.run{ | |||||
margin: auto; | |||||
margin-right: 30rpx; | |||||
height: 60rpx; | |||||
width: 60rpx; | |||||
border-radius: 50%; | |||||
border: 1px solid $uni-color; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
</style> |
@ -0,0 +1,153 @@ | |||||
<template> | |||||
<view class="page"> | |||||
<navbar title="编辑模板" leftClick @leftClick="$utils.navigateBack" /> | |||||
<view style="padding: 25rpx;background-color: #fff;"> | |||||
<canvas-drag ref="canvasRef" | |||||
id="canvas-drag" :graph="graph" | |||||
:width="width" :height="height" | |||||
@onDrawArrChange="onDrawArrChange" | |||||
enableUndo="true"></canvas-drag> | |||||
</view> | |||||
<view class="btn-list"> | |||||
<view class="uni-color-btn" @tap="onAddImage">新建签名</view> | |||||
<view class="uni-color-btn" @tap="onChangeBgImage">导入合同</view> | |||||
<view class="uni-color-btn" @tap="onUndo">后退</view> | |||||
<view class="uni-color-btn" @tap="onClearCanvas">清空</view> | |||||
<view class="uni-color-btn" @tap="submit">保存</view> | |||||
<!-- <view class="uni-color-btn" @tap="onExportJSON">导出模板</view> --> | |||||
<!-- <view class="uni-color-btn" @tap="onAddText">添加文字</view> --> | |||||
</view> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
import canvasDrag from "@/components/canvas-drag/index"; | |||||
import setData from "@/components/canvas-drag/setData.js"; | |||||
export default { | |||||
components: { | |||||
canvasDrag | |||||
}, | |||||
mixins : [setData], | |||||
data() { | |||||
return { | |||||
height: 750, | |||||
width: 700, | |||||
drawArr : [], | |||||
} | |||||
}, | |||||
onLoad() { | |||||
}, | |||||
mounted() {}, | |||||
methods: { | |||||
// 签名位置更新时 | |||||
onDrawArrChange(arr){ | |||||
this.drawArr = arr | |||||
}, | |||||
/** | |||||
* 添加签名位置 | |||||
*/ | |||||
onAddImage() { | |||||
wx.chooseImage({ | |||||
success: res => { | |||||
this.setData({ | |||||
graph: { | |||||
w: 100, | |||||
h: 50, | |||||
type: 'image', | |||||
url: res.tempFilePaths[0] | |||||
} | |||||
}); | |||||
} | |||||
}); | |||||
}, | |||||
/** | |||||
* 导入合同 | |||||
*/ | |||||
onChangeBgImage() { | |||||
let self = this | |||||
wx.chooseImage({ | |||||
success: res => { | |||||
uni.getImageInfo({ | |||||
src: res.tempFilePaths[0], | |||||
success: image => { | |||||
console.log(image); | |||||
let allwh = image.height + image.width | |||||
let imgw = (image.width / allwh).toFixed(2) | |||||
let imgh = (image.height / allwh).toFixed(2) | |||||
self.height = imgh * Math.ceil(this.width / imgw) | |||||
self.$nextTick(() => { | |||||
this.$refs.canvasRef.changeBgImage(image.path) | |||||
}) | |||||
} | |||||
}) | |||||
} | |||||
}); | |||||
}, | |||||
// 后退 | |||||
onUndo(event) { | |||||
this.$refs.canvasRef.undo(); | |||||
}, | |||||
/** | |||||
* 导出当前画布为模板 | |||||
*/ | |||||
onExportJSON() { | |||||
this.$refs.canvasRef.exportFun().then(imgArr => { | |||||
console.log(imgArr); | |||||
uni.previewImage({ | |||||
urls: [imgArr], | |||||
current: 0, | |||||
}) | |||||
}).catch(e => { | |||||
console.error(e); | |||||
}); | |||||
}, | |||||
/** | |||||
* 添加文本 | |||||
*/ | |||||
onAddText() { | |||||
this.setData({ | |||||
graph: { | |||||
type: 'text', | |||||
text: 'helloworld' | |||||
} | |||||
}); | |||||
}, | |||||
onClearCanvas(event) { | |||||
let _this = this; | |||||
_this.setData({ | |||||
canvasBg: null | |||||
}); | |||||
this.$refs.canvasRef.clearCanvas(); | |||||
}, | |||||
submit() { | |||||
uni.setStorageSync('drawArrJson', JSON.stringify(this.drawArr)) | |||||
uni.navigateBack(-1) | |||||
}, | |||||
} | |||||
} | |||||
</script> | |||||
<style scoped lang="scss"> | |||||
.page { | |||||
padding-bottom: 100rpx; | |||||
.btn-list { | |||||
display: flex; | |||||
flex-wrap: wrap; | |||||
padding: 20rpx; | |||||
gap: 20rpx; | |||||
.uni-color-btn { | |||||
margin: 0; | |||||
border-radius: 10rpx; | |||||
} | |||||
} | |||||
} | |||||
</style> |
@ -0,0 +1,188 @@ | |||||
<template> | |||||
<div class="contain"> | |||||
<navbar | |||||
title="电子签名" | |||||
v-if="!crosswise" | |||||
leftClick | |||||
@leftClick="$utils.navigateBack"/> | |||||
<view | |||||
class="electronic-contract" | |||||
:class="{crosswise}"> | |||||
<canvas | |||||
canvas-id="mycanvas" | |||||
class="mycanvas" | |||||
:style="{height, width}" | |||||
disable-scroll="true" | |||||
@touchstart="ontouchstart" | |||||
@touchmove="touchmove"></canvas> | |||||
<view class="title" | |||||
v-if="crosswise"> | |||||
请旋转屏幕,按文字方向开始签名 | |||||
</view> | |||||
</view> | |||||
<view class="btn-list"> | |||||
<view class="uni-color-btn" @click="clear"> | |||||
清除 | |||||
</view> | |||||
<view class="uni-color-btn" @click="close"> | |||||
完成 | |||||
</view> | |||||
</view> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
data() { | |||||
return { | |||||
oc: null, | |||||
points: [], //路径点集合 | |||||
show: true, | |||||
height : '350rpx', | |||||
width : '700rpx', | |||||
crosswise : false, | |||||
options : {}, | |||||
} | |||||
}, | |||||
onReady() { | |||||
this.open() | |||||
}, | |||||
onLoad(options) { | |||||
this.options = options | |||||
}, | |||||
onShow() { | |||||
this.initPM() | |||||
}, | |||||
methods: { | |||||
initPM(){ | |||||
if(!this.crosswise) return | |||||
let info = uni.getSystemInfoSync(); | |||||
console.log(info.windowWidth); | |||||
console.log(info.windowHeight); | |||||
}, | |||||
open() { | |||||
this.initCanvas(); | |||||
}, | |||||
close() { | |||||
uni.canvasToTempFilePath({ | |||||
canvasId: "mycanvas", | |||||
success: (res) => { | |||||
console.log(res.tempFilePath); | |||||
const canvas = res.tempFilePath; | |||||
uni.setStorageSync('electronicSignature', canvas) | |||||
let o = this.options | |||||
uni.navigateBack(-1) | |||||
}, | |||||
fail(error) { | |||||
console.error("转化图片错误!", error) | |||||
} | |||||
}); | |||||
}, | |||||
initCanvas() { | |||||
this.oc = uni.createCanvasContext("mycanvas"); | |||||
}, | |||||
ontouchstart(e) { | |||||
this.points = [] | |||||
const startX = e.changedTouches[0].x; | |||||
const startY = e.changedTouches[0].y; | |||||
let startPoint = { | |||||
X: startX, | |||||
Y: startY, | |||||
}; | |||||
this.points.push(startPoint); | |||||
}, | |||||
touchmove(e) { | |||||
let moveX = e.changedTouches[0].x; | |||||
let moveY = e.changedTouches[0].y; | |||||
let movePoint = { | |||||
X: moveX, | |||||
Y: moveY, | |||||
}; | |||||
this.points.push(movePoint); | |||||
let len = this.points.length; | |||||
if (len >= 2) { | |||||
this.draw(); //绘制路径 | |||||
} | |||||
}, | |||||
draw() { | |||||
if (this.points.length < 2) return; | |||||
this.oc.beginPath(); | |||||
this.oc.moveTo(this.points[0].X, this.points[0].Y); | |||||
// for (let i = 1; i < this.points.length; i++) { | |||||
// let point = this.points[i]; | |||||
// this.oc.lineTo(point.X, point.Y); | |||||
// } | |||||
this.oc.lineTo(this.points[1].X, this.points[1].Y); | |||||
this.points.shift() | |||||
this.oc.setStrokeStyle('#000000'); | |||||
this.oc.setLineWidth(5); | |||||
this.oc.setLineCap('round'); | |||||
this.oc.setLineJoin('round'); | |||||
this.oc.stroke(); | |||||
this.oc.draw(true); | |||||
}, | |||||
//清空画布 | |||||
clear() { | |||||
let that = this; | |||||
uni.getSystemInfo({ | |||||
success: (res) => { | |||||
let canvasw = res.windowWidth; | |||||
let canvash = res.windowHeight; | |||||
that.oc.clearRect(0, 0, canvasw, canvash); | |||||
that.oc.draw(true); | |||||
}, | |||||
}); | |||||
}, | |||||
} | |||||
}; | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
.contain { | |||||
position: relative; | |||||
.mycanvas { | |||||
background-color: #fff; | |||||
border-radius: 20rpx; | |||||
} | |||||
.electronic-contract{ | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
margin: 25rpx; | |||||
} | |||||
.crosswise{ | |||||
.title{ | |||||
writing-mode: vertical-lr; /* 垂直从上到下 */ | |||||
transform: skewY(180deg); /* 颠倒文本 */ | |||||
display: inline-block; /* 需要设置为内联块 */ | |||||
} | |||||
} | |||||
.btn-list { | |||||
display: flex; | |||||
padding: 20rpx; | |||||
justify-content: space-around; | |||||
.uni-color-btn { | |||||
border-radius: 10rpx; | |||||
margin: 0; | |||||
padding: 20rpx 40rpx; | |||||
} | |||||
} | |||||
} | |||||
</style> |
@ -1,44 +0,0 @@ | |||||
<template> | |||||
<view class="page"> | |||||
<navbar title="电子合同" | |||||
leftClick | |||||
@leftClick="$utils.navigateBack"/> | |||||
</view> | |||||
</template> | |||||
<script> | |||||
export default { | |||||
data() { | |||||
return { | |||||
id : 0, | |||||
detail : {}, | |||||
} | |||||
}, | |||||
onLoad({id}) { | |||||
this.id = id | |||||
this.getDetail() | |||||
}, | |||||
methods: { | |||||
getDetail(){ | |||||
let data = { | |||||
contractId: this.id | |||||
} | |||||
if (uni.getStorageSync('token')) { | |||||
// data.token = uni.getStorageSync('token') | |||||
} | |||||
this.$api('queryContracById', data, res => { | |||||
if (res.code == 200) { | |||||
this.detail = res.result | |||||
} | |||||
}) | |||||
}, | |||||
} | |||||
} | |||||
</script> | |||||
<style scoped lang="scss"> | |||||
</style> |