319 lines
6.7 KiB
Vue
319 lines
6.7 KiB
Vue
<template>
|
||
<view class="page">
|
||
<cu-custom bgColor="bg-blue" :isBack="true"><block slot="backText">返回</block><block slot="content">签名确认</block></cu-custom>
|
||
|
||
<view class="sign-container">
|
||
<view class="sign-info">
|
||
<text class="info-label">交接人:</text>
|
||
<text class="info-value">{{handName}}</text>
|
||
</view>
|
||
|
||
<!-- 签名区域 -->
|
||
<view class="signature-section" @touchstart="preventScroll" @touchmove="preventScroll">
|
||
<view class="signature-header">
|
||
<text class="signature-title">请签名</text>
|
||
<text class="signature-clear" v-if="hasSigned" @click="clearCanvas">点击重签</text>
|
||
</view>
|
||
<view class="canvas-wrapper">
|
||
<canvas canvas-id="signCanvas" id="signCanvas" class="canvas-area"
|
||
@touchstart="touchStart"
|
||
@touchmove="touchMove"
|
||
@touchend="touchEnd"></canvas>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="btn-container">
|
||
<button class="clear-btn" @click="clearCanvas">清除</button>
|
||
<button class="confirm-btn" @click="confirmSign">确认</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
id: '',
|
||
handName: '',
|
||
canvasCtx: null,
|
||
canvasWidth: 0,
|
||
canvasHeight: 0,
|
||
canvasLeft: 0,
|
||
canvasTop: 0,
|
||
isDrawing: false,
|
||
points: []
|
||
}
|
||
},
|
||
computed: {
|
||
hasSigned() {
|
||
return this.points.length > 5
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
console.log('接收到的参数:', options);
|
||
this.id = decodeURIComponent(options.id || '');
|
||
this.handName = decodeURIComponent(options.handName || '');
|
||
console.log('id:', this.id, 'handName:', this.handName);
|
||
},
|
||
onReady() {
|
||
this.initCanvas();
|
||
},
|
||
methods: {
|
||
initCanvas() {
|
||
const ctx = uni.createCanvasContext('signCanvas', this);
|
||
this.canvasCtx = ctx;
|
||
|
||
// 获取canvas尺寸和位置
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query.select('#signCanvas').boundingClientRect((res) => {
|
||
if (res) {
|
||
this.canvasWidth = res.width;
|
||
this.canvasHeight = res.height;
|
||
this.canvasLeft = res.left;
|
||
this.canvasTop = res.top;
|
||
}
|
||
}).exec();
|
||
},
|
||
|
||
// 阻止页面滚动
|
||
preventScroll(e) {
|
||
// 空函数即可阻止
|
||
},
|
||
|
||
// 获取触摸位置(相对canvas的坐标)
|
||
getTouchPos(e) {
|
||
const touch = e.touches[0];
|
||
// 转换为canvas相对坐标
|
||
return {
|
||
x: touch.clientX - this.canvasLeft,
|
||
y: touch.clientY - this.canvasTop
|
||
};
|
||
},
|
||
|
||
// 触摸开始
|
||
touchStart(e) {
|
||
e.preventDefault();
|
||
// 先更新canvas位置
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query.select('#signCanvas').boundingClientRect((res) => {
|
||
if (res) {
|
||
this.canvasLeft = res.left;
|
||
this.canvasTop = res.top;
|
||
}
|
||
}).exec();
|
||
|
||
const pos = this.getTouchPos(e);
|
||
this.points = [pos];
|
||
this.isDrawing = true;
|
||
},
|
||
|
||
// 触摸移动
|
||
touchMove(e) {
|
||
e.preventDefault();
|
||
if (!this.isDrawing) return;
|
||
const pos = this.getTouchPos(e);
|
||
this.points.push(pos);
|
||
|
||
// 绘制
|
||
if (this.points.length >= 2 && this.canvasCtx) {
|
||
this.canvasCtx.beginPath();
|
||
this.canvasCtx.setStrokeStyle('#333');
|
||
this.canvasCtx.setLineWidth(3);
|
||
this.canvasCtx.setLineCap('round');
|
||
this.canvasCtx.setLineJoin('round');
|
||
this.canvasCtx.moveTo(this.points[this.points.length - 2].x, this.points[this.points.length - 2].y);
|
||
this.canvasCtx.lineTo(pos.x, pos.y);
|
||
this.canvasCtx.stroke();
|
||
this.canvasCtx.draw(true);
|
||
}
|
||
},
|
||
|
||
// 触摸结束
|
||
touchEnd() {
|
||
this.isDrawing = false;
|
||
},
|
||
|
||
// 清空签字
|
||
clearCanvas() {
|
||
this.points = [];
|
||
const ctx = uni.createCanvasContext('signCanvas', this);
|
||
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||
ctx.draw();
|
||
},
|
||
|
||
confirmSign() {
|
||
if (!this.hasSigned) {
|
||
uni.showToast({
|
||
title: '请先签名',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
uni.canvasToTempFilePath({
|
||
canvasId: 'signCanvas',
|
||
fileType: 'png',
|
||
quality: 1,
|
||
success: (res) => {
|
||
console.log('签名图片路径:', res.tempFilePath);
|
||
uni.showToast({
|
||
title: '签名已确认',
|
||
icon: 'success',
|
||
duration: 2000
|
||
});
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 500);
|
||
},
|
||
fail: (err) => {
|
||
console.error('保存签名失败', err);
|
||
uni.showToast({
|
||
title: '签名保存失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
}, this);
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.page {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
/* 签名容器 */
|
||
.sign-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: calc(100vh - 100rpx);
|
||
padding: 30rpx;
|
||
}
|
||
|
||
/* 签名信息 */
|
||
.sign-info {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
margin-bottom: 30rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: 32rpx;
|
||
color: #666;
|
||
font-weight: 500;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 32rpx;
|
||
color: #4a90e2;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 签名区域样式 */
|
||
.signature-section {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 30rpx;
|
||
padding: 20rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.signature-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 20rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.signature-title {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.signature-clear {
|
||
font-size: 24rpx;
|
||
color: #4a90e2;
|
||
}
|
||
|
||
/* 画布包裹层 */
|
||
.canvas-wrapper {
|
||
flex: 1;
|
||
background: #fff;
|
||
border: 2rpx dashed #ccc;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
/* 签名画布 */
|
||
.canvas-area {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #fafafa;
|
||
}
|
||
|
||
/* 按钮容器 */
|
||
.btn-container {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
padding: 30rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 清除按钮 */
|
||
.clear-btn {
|
||
flex: 1;
|
||
margin-right: 20rpx;
|
||
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
|
||
color: #666;
|
||
border: none;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx 0;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.clear-btn::after {
|
||
border: none;
|
||
}
|
||
|
||
/* 确认按钮 */
|
||
.confirm-btn {
|
||
flex: 1;
|
||
margin-left: 20rpx;
|
||
background: linear-gradient(135deg, #4a90e2 0%, #357abd 100%);
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx 0;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
box-shadow: 0 6rpx 24rpx rgba(74, 144, 226, 0.35);
|
||
}
|
||
|
||
.confirm-btn::after {
|
||
border: none;
|
||
}
|
||
</style>
|