| @ -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> | |||