出库逻辑添加,扫码识别编码成功,后续对应逻辑没有完成
This commit is contained in:
135
inventory-web/src/components/Signature/index.vue
Normal file
135
inventory-web/src/components/Signature/index.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="signature-container">
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
@mousedown="startDrawing"
|
||||
@mousemove="draw"
|
||||
@mouseup="stopDrawing"
|
||||
@mouseleave="stopDrawing"
|
||||
@touchstart.prevent="startDrawing"
|
||||
@touchmove.prevent="draw"
|
||||
@touchend.prevent="stopDrawing"
|
||||
></canvas>
|
||||
<div class="actions">
|
||||
<el-button size="small" @click="clear">重签</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
const isDrawing = ref(false)
|
||||
const ctx = ref<CanvasRenderingContext2D | null>(null)
|
||||
|
||||
// 初始化 Canvas
|
||||
onMounted(() => {
|
||||
if (canvasRef.value) {
|
||||
const canvas = canvasRef.value
|
||||
// 设置画布大小 (可以根据父容器调整)
|
||||
canvas.width = canvas.offsetWidth
|
||||
canvas.height = 300 // 固定高度
|
||||
ctx.value = canvas.getContext('2d')
|
||||
if (ctx.value) {
|
||||
ctx.value.lineWidth = 3
|
||||
ctx.value.lineCap = 'round'
|
||||
ctx.value.strokeStyle = '#000'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 获取坐标 (兼容鼠标和触摸)
|
||||
const getPos = (e: MouseEvent | TouchEvent) => {
|
||||
const canvas = canvasRef.value
|
||||
if (!canvas) return { x: 0, y: 0 }
|
||||
|
||||
const rect = canvas.getBoundingClientRect()
|
||||
let clientX, clientY
|
||||
|
||||
if ('touches' in e) {
|
||||
clientX = e.touches[0].clientX
|
||||
clientY = e.touches[0].clientY
|
||||
} else {
|
||||
clientX = (e as MouseEvent).clientX
|
||||
clientY = (e as MouseEvent).clientY
|
||||
}
|
||||
|
||||
return {
|
||||
x: clientX - rect.left,
|
||||
y: clientY - rect.top
|
||||
}
|
||||
}
|
||||
|
||||
const startDrawing = (e: MouseEvent | TouchEvent) => {
|
||||
isDrawing.value = true
|
||||
const { x, y } = getPos(e)
|
||||
ctx.value?.beginPath()
|
||||
ctx.value?.moveTo(x, y)
|
||||
}
|
||||
|
||||
const draw = (e: MouseEvent | TouchEvent) => {
|
||||
if (!isDrawing.value) return
|
||||
const { x, y } = getPos(e)
|
||||
ctx.value?.lineTo(x, y)
|
||||
ctx.value?.stroke()
|
||||
}
|
||||
|
||||
const stopDrawing = () => {
|
||||
isDrawing.value = false
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
if (canvasRef.value && ctx.value) {
|
||||
ctx.value.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出签名为 File 对象
|
||||
*/
|
||||
const generateFile = (): Promise<File | null> => {
|
||||
return new Promise((resolve) => {
|
||||
if (!canvasRef.value) {
|
||||
resolve(null)
|
||||
return
|
||||
}
|
||||
canvasRef.value.toBlob((blob) => {
|
||||
if (blob) {
|
||||
const file = new File([blob], `sign_${Date.now()}.png`, { type: 'image/png' })
|
||||
resolve(file)
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
}, 'image/png')
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
clear,
|
||||
generateFile
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.signature-container {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #f5f7fa;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
width: 100%; /* 响应式宽度 */
|
||||
height: 300px;
|
||||
cursor: crosshair;
|
||||
background: #fff;
|
||||
}
|
||||
.actions {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user