<template>
|
<view class="signature-container">
|
<view class="canvas-container">
|
<canvas canvas-id="signatureCanvas" id="signatureCanvas" class="signature-canvas"
|
@touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd"
|
disable-scroll></canvas>
|
<view class="toast" v-if="toastVisible">请签写您的名字</view>
|
</view>
|
<view class="controls">
|
<view></view>
|
<view class="btn-group">
|
<button class="btn btn-clear" @tap="clearCanvas" :disabled="uploading">清空</button>
|
<button class="btn btn-confirm" @tap="saveSignature" :disabled="uploading || !hasDrawn">
|
{{ uploading ? '上传中...' : '确定' }}
|
</button>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
import VUE_APP_BASE_URL from "../../common/constant";
|
import {
|
postSignaure
|
} from "../../api/review.js"
|
export default {
|
name: 'SignaturePad',
|
data() {
|
return {
|
canvas: null,
|
ctx: null,
|
isDrawing: false,
|
hasDrawn: false,
|
lastX: 0,
|
lastY: 0,
|
toastVisible: true,
|
systemInfo: null,
|
uploading: false,
|
id: null
|
}
|
},
|
onReady() {
|
this.initCanvas();
|
},
|
onLoad(e) {
|
this.getSystemInfo();
|
this.id = e.id && JSON.parse(decodeURIComponent(e.id))
|
},
|
onUnload() {
|
this.cleanup();
|
},
|
methods: {
|
getSystemInfo() {
|
const that = this;
|
wx.getSystemInfo({
|
success(res) {
|
that.systemInfo = res;
|
that.initCanvas();
|
}
|
});
|
},
|
|
initCanvas() {
|
if (!this.systemInfo) return;
|
this.ctx = wx.createCanvasContext('signatureCanvas', this);
|
this.ctx.setLineCap('round');
|
this.ctx.setLineJoin('round');
|
this.ctx.setStrokeStyle('#1A1A1A');
|
this.ctx.setLineWidth(2);
|
this.ctx.setFillStyle('#1A1A1A');
|
this.clearCanvas();
|
},
|
|
handleTouchStart(e) {
|
if (!this.ctx) return;
|
|
const touch = e.touches[0];
|
this.isDrawing = true;
|
[this.lastX, this.lastY] = [touch.x, touch.y];
|
this.drawDot(this.lastX, this.lastY);
|
this.hideToast();
|
this.hasDrawn = true;
|
},
|
|
handleTouchMove(e) {
|
if (!this.isDrawing || !this.ctx) return;
|
|
const touch = e.touches[0];
|
const x = touch.x;
|
const y = touch.y;
|
|
// 绘制线条
|
this.ctx.beginPath();
|
this.ctx.moveTo(this.lastX, this.lastY);
|
this.ctx.lineTo(x, y);
|
this.ctx.stroke();
|
this.ctx.draw(true);
|
|
this.drawDot(x, y);
|
|
[this.lastX, this.lastY] = [x, y];
|
},
|
|
handleTouchEnd() {
|
this.isDrawing = false;
|
},
|
|
drawDot(x, y) {
|
if (!this.ctx) return;
|
|
this.ctx.beginPath();
|
this.ctx.arc(x, y, 1, 0, 2 * Math.PI);
|
this.ctx.fill();
|
this.ctx.draw(true);
|
},
|
|
clearCanvas() {
|
if (!this.ctx) return;
|
|
|
this.ctx.clearRect(0, 0, 1000, 1000);
|
this.ctx.setFillStyle('#F5F7FB');
|
this.ctx.fillRect(0, 0, 1000, 1000);
|
this.ctx.draw(true);
|
|
this.hasDrawn = false;
|
this.showToast();
|
},
|
|
showToast() {
|
this.toastVisible = true;
|
},
|
|
hideToast() {
|
this.toastVisible = false;
|
},
|
|
async saveSignature() {
|
if (!this.hasDrawn) {
|
wx.showToast({
|
title: '您还未签名!',
|
icon: 'none',
|
duration: 2000
|
});
|
return;
|
}
|
if (this.uploading) return;
|
this.uploading = true;
|
try {
|
const tempFilePath = await this.canvasToTempFile();
|
const uploadResult = await this.uploadToServer(tempFilePath);
|
await this.handleUploadSuccess(uploadResult);
|
|
} catch (error) {
|
this.handleUploadError(error);
|
} finally {
|
this.uploading = false;
|
}
|
},
|
|
canvasToTempFile() {
|
return new Promise((resolve, reject) => {
|
wx.canvasToTempFilePath({
|
canvasId: 'signatureCanvas',
|
quality: 1,
|
fileType: 'png',
|
success: (res) => {
|
resolve(res.tempFilePath);
|
},
|
fail: (err) => {
|
reject(new Error('生成图片失败:' + JSON.stringify(err)));
|
}
|
}, this);
|
});
|
},
|
|
uploadToServer(tempFilePath) {
|
return new Promise((resolve, reject) => {
|
wx.uploadFile({
|
url: `${VUE_APP_BASE_URL}/system/common/uploadFile`, // 替换为你的上传接口
|
filePath: tempFilePath,
|
name: 'file',
|
formData: {},
|
header: {
|
'Authorization': uni.getStorageSync('tk'), // 如果有token认证
|
'Content-Type': 'multipart/form-data'
|
},
|
success: (res) => {
|
if (res.statusCode === 200) {
|
try {
|
const data = JSON.parse(res.data);
|
resolve(data);
|
} catch (e) {
|
reject(new Error('解析响应数据失败'));
|
}
|
} else {
|
reject(new Error(`上传失败,状态码:${res.statusCode}`));
|
}
|
},
|
fail: (err) => {
|
reject(new Error('网络请求失败:' + JSON.stringify(err)));
|
}
|
});
|
});
|
},
|
|
async handleUploadSuccess(res) {
|
try {
|
if (!res.data || !res.data.path) {
|
throw new Error('未获取到文件路径');
|
}
|
const filePath = res.data.path;
|
const submitResult = await this.submitSignatureInfo(filePath);
|
this.handleFinalSuccess(submitResult);
|
} catch (error) {
|
throw new Error('提交签名信息失败:' + error.message);
|
}
|
},
|
|
async submitSignatureInfo(path) {
|
const res = await postSignaure({
|
id: this.id,
|
sign: path
|
})
|
if (res.code == 200) {
|
return res
|
} else {
|
reject(new Error(`提交失败,状态码:${res.code}`));
|
}
|
},
|
|
handleFinalSuccess(result) {
|
wx.showToast({
|
title: '签名提交成功',
|
icon: 'success',
|
duration: 2000
|
});
|
setTimeout(() => {
|
// const pages = getCurrentPages();
|
// if (pages.length > 1) {
|
// const prevPage = pages[pages.length - 2];
|
// if (prevPage) {
|
// prevPage.needRefresh = true;
|
// }
|
// wx.navigateBack({
|
// delta: 1,
|
// success: () => {
|
// console.log('返回成功,页面应该刷新');
|
// }
|
// });
|
// } else {
|
wx.reLaunch({
|
url: '/pages/review/index'
|
});
|
// }
|
}, 1500);
|
},
|
|
handleUploadError(error) {
|
console.error('上传失败:', error);
|
|
wx.showToast({
|
title: '上传失败,请重试',
|
icon: 'none',
|
duration: 2000
|
});
|
},
|
|
cleanup() {
|
this.ctx = null;
|
this.isDrawing = false;
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.signature-container {
|
width: 100vw;
|
height: 100vh;
|
display: flex;
|
flex-direction: column;
|
background: #ffffff;
|
}
|
|
.canvas-container {
|
flex: 1;
|
position: relative;
|
background: #F5F7FB;
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
border-radius: 4px;
|
margin: 10px;
|
overflow: hidden;
|
}
|
|
.signature-canvas {
|
width: 100%;
|
height: 100%;
|
background: #F5F7FB;
|
}
|
|
.toast {
|
position: absolute;
|
top: 50%;
|
left: 40%;
|
transform: translate(-50%, -50%);
|
transform: rotate(90deg);
|
font-size: 40rpx;
|
color: rgba(58, 65, 85, 0.4);
|
pointer-events: none;
|
z-index: 100;
|
}
|
|
.controls {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 15px 32px;
|
background: #ffffff;
|
border-top: 1px solid #e5e7eb;
|
}
|
|
.btn {
|
padding: 8px 20px;
|
border: none;
|
border-radius: 4px;
|
font-size: 14px;
|
cursor: pointer;
|
transition: all 0.3s ease;
|
}
|
|
.btn-clear {
|
background: rgba(103, 149, 255, 0.2);
|
color: #3670F5;
|
margin-right: 12px;
|
}
|
|
.btn-clear:hover {
|
background: rgba(103, 149, 255, 0.3);
|
}
|
|
.btn-confirm {
|
background: #3670F5;
|
color: #FFFFFF;
|
}
|
|
.btn-confirm:hover {
|
background: #2563eb;
|
}
|
|
.btn-group {
|
display: flex;
|
gap: 10px;
|
}
|
|
/* 微信小程序适配样式 */
|
button {
|
margin: 0;
|
padding: 8px 20px;
|
border-radius: 4px;
|
font-size: 14px;
|
}
|
|
button::after {
|
border: none;
|
}
|
|
/* 横竖屏适配 */
|
@media (orientation: landscape) {
|
.signature-container {
|
flex-direction: column;
|
}
|
|
.canvas-container {
|
height: calc(100vh - 80px);
|
}
|
}
|
|
@media (orientation: portrait) {
|
.signature-container {
|
flex-direction: column;
|
}
|
|
.canvas-container {
|
height: calc(100vh - 120px);
|
}
|
}
|
</style>
|