<template>
|
<div class="signature">
|
<el-row justify="space-between">
|
<el-text>签名</el-text>
|
<el-text v-if="imageUrl" @click="imageUrl=''">清除签名</el-text>
|
<el-text v-else @click="signatureDialog=true">点击签名</el-text>
|
</el-row>
|
<el-image v-if="imageUrl" :src="imageUrl"></el-image>
|
<div v-else class="image-slot"></div>
|
|
<el-dialog
|
v-model="signatureDialog"
|
fullscreen
|
:show-close="false"
|
class="p-0"
|
>
|
<div class="signature_content p-4" :class="{'rotate_90': xsOnly}" ref="signBox" @click="endMove">
|
<el-row justify="space-between">
|
<el-col :span="3"></el-col>
|
<el-col :span="18">
|
<el-row justify="center">
|
<el-text class=" text-xl font-bold">手写签名</el-text>
|
</el-row>
|
</el-col>
|
<el-col :span="3">
|
<el-row justify="end">
|
<el-button text @click="signatureDialog=false">
|
<Icon icon="material-symbols:close-rounded" width="24" height="24" style="color: black" />
|
</el-button>
|
</el-row>
|
</el-col>
|
</el-row>
|
<div class="canvas_box_normal">
|
<canvas ref="signCanvas" style="background-color: white;"></canvas>
|
</div>
|
<el-row justify="end">
|
<el-button @click="clearCanvas()">重置</el-button>
|
<el-button type="primary" @click="confirm()">
|
确定
|
</el-button>
|
</el-row>
|
</div>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import { useWindowSize } from '@/utils/hook.js'
|
import { uploadByBase64 } from '@/utils/tool.js';
|
export default {
|
setup() {
|
const { width, height } = useWindowSize()
|
return { width, height }
|
},
|
data() {
|
return {
|
imageUrl: '',
|
signatureDialog: false,
|
editCanvas: null,
|
startDrawFlag: false,
|
drawingFlag: false,
|
canvasMarginY: 12,
|
canvasMarginX: 12,
|
flag: false
|
}
|
},
|
props: {
|
modelValue: {
|
type: String,
|
default: ''
|
}
|
},
|
computed: {
|
xsOnly: function() {
|
return this.width <= 576
|
}
|
},
|
watch: {
|
modelValue: function(val) {
|
this.imageUrl = val
|
},
|
width: function(){
|
this.initMobileStyle()
|
},
|
height: function(){
|
this.updateCanvasMargin()
|
},
|
xsOnly: function(){
|
this.updateCanvasMargin()
|
},
|
signatureDialog: function(val) {
|
if (val) {
|
this.$nextTick(() => {
|
this.initSignCanvas()
|
})
|
} else {
|
this.clearCanvas()
|
}
|
},
|
},
|
methods: {
|
initSignCanvas: function(){
|
if (this.editCanvas || !this.$refs.signCanvas) return true
|
|
this.editCanvas = this.$refs.signCanvas
|
|
this.initMobileStyle()
|
|
this.initCanvasStyle()
|
|
this.initCanvasEvent()
|
|
this.updateCanvasMargin()
|
},
|
initMobileStyle: function(){
|
if (!this.xsOnly) return false
|
|
this.$refs.signBox.style.transformOrigin = `${(window.innerWidth / 2 / window.innerHeight) * 100}%`
|
},
|
initCanvasStyle: function(){
|
let parentW = this.editCanvas.parentElement.offsetWidth - 16
|
let parentH = this.editCanvas.parentElement.offsetHeight - 48
|
|
let canvasW = Math.min(parentH * 3, 720)
|
let canvasH = Math.min(parentW / 3, 240)
|
if (canvasH > parentH) {
|
this.editCanvas.width = canvasW
|
this.editCanvas.height = canvasW / 3
|
} else {
|
this.editCanvas.height = canvasH
|
this.editCanvas.width = canvasH * 3
|
}
|
},
|
initCanvasEvent: function(){
|
this.editCanvas.onmousedown = this.startMove
|
this.editCanvas.onmousemove = this.moving
|
this.editCanvas.onmouseup = this.endMove
|
this.editCanvas.ontouchstart = this.startMove
|
this.editCanvas.ontouchmove = this.moving
|
this.editCanvas.ontouchend = this.endMove
|
},
|
updateCanvasMargin: function(){
|
this.canvasMarginY = (this.editCanvas.parentElement.offsetHeight - this.editCanvas.height) / 2
|
this.canvasMarginX = (this.editCanvas.parentElement.offsetWidth - this.editCanvas.width) / 2
|
},
|
startMove: function(event){
|
this.drawingFlag = true
|
|
let { x, y } = this.getPosition(event)
|
let cxt = this.editCanvas.getContext('2d')
|
cxt.lineWidth = Math.max(this.editCanvas.width / 240, 2)
|
cxt.beginPath()
|
cxt.moveTo(x, y)
|
event.preventDefault()
|
},
|
moving: function(event){
|
if (!this.drawingFlag) return
|
|
this.startDrawFlag = true
|
|
let { x, y } = this.getPosition(event)
|
let cxt = this.editCanvas.getContext('2d')
|
cxt.lineTo(x, y)
|
cxt.stroke()
|
event.preventDefault()
|
},
|
getPosition: function(event){
|
let x, y
|
let touches = event.touches || event.targetTouches || [{}]
|
let positionX = touches[0].clientX || event.clientX
|
let positionY = touches[0].clientY || event.clientY
|
if (this.xsOnly) {
|
x = positionY - this.canvasMarginX
|
y = window.innerWidth - positionX - 48 - this.canvasMarginY
|
} else {
|
x = positionX - this.canvasMarginX - 16
|
y = positionY - this.canvasMarginY - 48
|
}
|
return { x, y }
|
},
|
endMove: function(){
|
this.drawingFlag = false
|
},
|
clearCanvas: function(){
|
this.startDrawFlag = false
|
let cxt = this.editCanvas.getContext('2d')
|
cxt.clearRect(0, 0, this.editCanvas.width, this.editCanvas.height);
|
},
|
async confirm() {
|
if (!this.startDrawFlag) {
|
this.$message.error('请先签名!')
|
return
|
}
|
let base64 = this.editCanvas.toDataURL('image/png', 1)
|
let smallBase64 = await this.resizedataURL(base64, 240, 80)
|
// let url = await uploadByBase64(smallBase64, '签名')
|
// if (!url) return false
|
this.imageUrl = smallBase64
|
this.signatureDialog = false
|
},
|
resizedataURL: function(base64, wantedWidth, wantedHeight){
|
return new Promise((resolve) => {
|
let img = document.createElement('img')
|
img.onload = function() {
|
let canvas = document.createElement('canvas')
|
canvas.width = wantedWidth
|
canvas.height = wantedHeight
|
let ctx = canvas.getContext('2d')
|
ctx.fillStyle = '#ffffff'
|
ctx.fillRect(0, 0, wantedWidth, wantedHeight);
|
ctx.drawImage(this, 0, 0, wantedWidth, wantedHeight)
|
resolve(canvas.toDataURL('image/png', 1))
|
}
|
img.src = base64
|
})
|
},
|
}
|
}
|
</script>
|
|
<style scoped>
|
.image-slot {
|
width: 100%;
|
max-width: 400px;
|
aspect-ratio: 16/9;
|
border: 1px solid #EAEAEA;
|
background: #F5F5F5F5;
|
margin-top: 10px;
|
border-radius: 8px;
|
}
|
.signature_content {
|
width: 100vw;
|
height: 100vh;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
}
|
.rotate_90 {
|
width: 100vh !important;
|
height: 100vw !important;
|
transform: rotate(90deg);
|
}
|
.canvas_box_normal {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
height: calc(100vh - 96px);
|
}
|
.signature :deep(.el-dialog__header) {
|
display: none;
|
}
|
.signature :deep(.el-dialog__body) {
|
width: 100vw;
|
height: 100vh;
|
overflow: hidden;
|
}
|
.signature :deep(.el-dialog) {
|
background: #f5f5f5;
|
}
|
|
</style>
|