first commit
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="h5-signature">
|
||||
<div v-if="!showSignaturePanel" class="main-panel">
|
||||
<slot v-if="$slots.default"></slot>
|
||||
<template v-else>
|
||||
<van-button @click="openSignature" class="open-button" size="middle" plain native-type="button">打开签字板</van-button>
|
||||
<van-image :src="signatureContent"></van-image>
|
||||
</template>
|
||||
</div>
|
||||
<van-popup v-model="showSignaturePanel" :style="{ height: '100vh', width: '100vw' }">
|
||||
<signature @save="save"></signature>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "h5",
|
||||
components: {
|
||||
signature: httpVueLoader("/components/plugins/sysSignature/index.vue?v=" + new Date().getTime())
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
this.signatureContent = val
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
signatureContent: null,
|
||||
showSignaturePanel: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSignature() {
|
||||
this.showSignaturePanel = true
|
||||
},
|
||||
|
||||
save(signature) {
|
||||
const file = this.base64ToFile(signature, "signature.png")
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
axios
|
||||
.post("/platform/signature/saveH5Signature", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
return res.data
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
alert("保存成功")
|
||||
this.showSignaturePanel = false
|
||||
this.$emit("input", res.data)
|
||||
} else {
|
||||
alert("保存失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
base64ToFile(base64Data, filename) {
|
||||
// 将base64的数据部分提取出来
|
||||
const parts = base64Data.split(";base64,")
|
||||
const contentType = parts[0].split(":")[1]
|
||||
const raw = window.atob(parts[1])
|
||||
const rawLength = raw.length
|
||||
const uInt8Array = new Uint8Array(rawLength)
|
||||
|
||||
for (let i = 0; i < rawLength; ++i) {
|
||||
uInt8Array[i] = raw.charCodeAt(i)
|
||||
}
|
||||
|
||||
// 使用Blob对象创建File对象
|
||||
const blob = new Blob([uInt8Array], { type: contentType })
|
||||
blob.lastModifiedDate = new Date()
|
||||
blob.name = filename
|
||||
return new File([blob], filename, { type: contentType })
|
||||
}
|
||||
},
|
||||
created() {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.h5-signature {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main-panel {
|
||||
position: relative;
|
||||
flex-direction: column-reverse;
|
||||
display: flex;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.main-panel .van-image {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.main-panel .open-button {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="signature-wrap">
|
||||
<canvas id="canvas"></canvas>
|
||||
<div :class="isLikelyMobile() ? 'action-buttons' : 'action-buttons pc-action-buttons'">
|
||||
<button @click="clear" class="action-button-danger" type="button">清空</button>
|
||||
<button @click="undo" class="action-button-warning" type="button">撤销</button>
|
||||
<button @click="save" class="action-button-primary" type="button">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// function a() {
|
||||
// // 检查当前屏幕方向
|
||||
// if (window.orientation === 0 || window.orientation === 180) {
|
||||
// // 竖屏状态
|
||||
// console.log("竖屏状态")
|
||||
// alert("当前不支持竖屏,请将设备调整为横屏!")
|
||||
// } else {
|
||||
// // 横屏状态,执行禁止旋转的操作
|
||||
// alert("当前不支持横屏,请将设备调整为竖屏!")
|
||||
// // 可以在这里使内容固定在竖屏方向
|
||||
// const innerWidth = window.innerWidth
|
||||
// const innerHeight = window.innerHeight
|
||||
// alert(innerHeight)
|
||||
// alert(innerWidth)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// window.addEventListener("orientationchange", a)
|
||||
// a()
|
||||
|
||||
let signature = null
|
||||
module.exports = {
|
||||
name: "sysSignature",
|
||||
data() {
|
||||
return {
|
||||
content: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
signature.clear()
|
||||
},
|
||||
undo() {
|
||||
signature.undo()
|
||||
},
|
||||
rotate() {
|
||||
signature.getRotateCanvas(90)
|
||||
},
|
||||
isLikelyMobile() {
|
||||
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
const isSmallScreen = window.screen.width <= 768;
|
||||
const hasMobileUA = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
|
||||
return hasMobileUA || (isTouchDevice && isSmallScreen);
|
||||
},
|
||||
save() {
|
||||
if (signature.isEmpty()) {
|
||||
alert("请签字后再保存")
|
||||
return
|
||||
}
|
||||
// const png = signature.getPNG()
|
||||
//旋转一下 横过来
|
||||
const rotateCanvas = signature.getRotateCanvas(-90)
|
||||
if (this.isLikelyMobile()) {
|
||||
this.$emit("save", rotateCanvas.toDataURL())
|
||||
} else {
|
||||
this.$emit("save", signature.toDataURL())
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const innerWidth = window.innerWidth
|
||||
const innerHeight = window.innerHeight
|
||||
|
||||
const canvas = document.getElementById("canvas")
|
||||
canvas.width = window.innerWidth
|
||||
canvas.height = window.innerHeight
|
||||
signature = new SmoothSignature(canvas, {
|
||||
width: innerWidth - 30,
|
||||
height: innerHeight - 30,
|
||||
scale: 2,
|
||||
minWidth: 4,
|
||||
maxWidth: 10,
|
||||
color: "#000000"
|
||||
// bgColor: "#f6f6f6"
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.signature-wrap {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 15px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.signature-wrap .action {
|
||||
width: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.signature-wrap .action-buttons {
|
||||
white-space: nowrap;
|
||||
transform: rotate(90deg);
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 60px;
|
||||
transform: rotate(90deg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
.signature-wrap .pc-action-buttons {
|
||||
bottom: 50px;
|
||||
right: 50px;
|
||||
left: unset;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.action-buttons button {
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
padding: 3px 12px;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.15s ease;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.action-button-primary {
|
||||
background-color: #1677ff;
|
||||
border: 1px solid #1677ff;
|
||||
}
|
||||
|
||||
.action-button-danger {
|
||||
background-color: #ff3141;
|
||||
border: 1px solid #ff3141;
|
||||
}
|
||||
|
||||
.action-button-warning {
|
||||
background-color: #ff8f1f;
|
||||
border: 1px solid #ff8f1f;
|
||||
}
|
||||
|
||||
.signature-wrap canvas {
|
||||
flex: 1;
|
||||
border-radius: 10px;
|
||||
border: 2px dashed #ccc;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<el-card shadow="never" class="pc-signature-card">
|
||||
<div class="pc-signature">
|
||||
<el-link type="primary">扫描下方二维码进行签字</el-link>
|
||||
<div class="signature-body" v-loading="loading">
|
||||
<QrCode v-if="!signatureContent" :options="{ width: 126 }" :value="signatureAddress"
|
||||
class="signature-qrcode"></QrCode>
|
||||
<el-image v-if="signatureContent" :src="signatureContent" class="signature-image"></el-image>
|
||||
</div>
|
||||
<el-link type="danger" icon="el-icon-edit" @click="signatureAgain" v-if="signatureContent">重签</el-link>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "sysSignaturePc",
|
||||
store,
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.signatureContent = val
|
||||
this.$emit("input", this.signatureContent)
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//前端唯一标识 当然后端还有用户id作为唯一标识
|
||||
id: new Date().getTime() + Math.floor(Math.random() * 10000) + 1,
|
||||
signatureAddress: null,
|
||||
interval: null,
|
||||
signatureContent: null,
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
const {code, data} = await this.$axios.post("/platform/signature/get")
|
||||
if (code === 0) {
|
||||
this.signatureContent = data && data.signature
|
||||
this.$emit("input", this.signatureContent)
|
||||
}
|
||||
this.signatureAddress = origin + "/platform/signature/scanPcCode?id=" + this.id
|
||||
},
|
||||
signatureAgain() {
|
||||
const val = sessionStorage.getItem("h5-scan-code-signature-" + this.id)
|
||||
this.signatureContent = val
|
||||
this.$emit("input", val)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.init()
|
||||
webSocketPubSub.subscribe("h5-scan-code-signature", (data) => {
|
||||
console.info("ws:收到签字成功消息:", data)
|
||||
this.signatureContent = data.value
|
||||
this.$emit("input", data.value)
|
||||
})
|
||||
|
||||
// 可选:轮询或 watch ready 状态
|
||||
const checkReady = () => {
|
||||
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
|
||||
console.info("ws.readyState is open");
|
||||
this.loading = false;
|
||||
console.log(this.signatureAddress)
|
||||
} else {
|
||||
setTimeout(checkReady, 100);
|
||||
}
|
||||
};
|
||||
checkReady();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pc-signature-card {
|
||||
border: 1px solid var(--border-color-base);
|
||||
}
|
||||
|
||||
.pc-signature {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
.signature-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.signature-qrcode {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.signature-image {
|
||||
height: 100px;
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user