130 lines
3.2 KiB
Vue

<script setup>
import { ref } from 'vue';
import { QrcodeStream } from 'vue-qrcode-reader';
/** Props y Emits */
const props = defineProps({
modelValue: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue', 'qr-detected']);
/** Refs */
const error = ref('');
const isScanning = ref(true);
/** Métodos */
function paintBoundingBox(detectedCodes, ctx) {
for (const detectedCode of detectedCodes) {
const {
boundingBox: { x, y, width, height }
} = detectedCode;
ctx.lineWidth = 4;
ctx.strokeStyle = '#10b981';
ctx.strokeRect(x, y, width, height);
}
}
function onDetect(detectedCodes) {
if (detectedCodes.length > 0) {
const code = detectedCodes[0].rawValue;
// Emitir el código escaneado al componente padre
emit('update:modelValue', code);
// Pausar el escaneo después de detectar
isScanning.value = false;
// Emitir evento con el código detectado
emit('qr-detected', code);
// Reactivar el escaneo después de 2 segundos
setTimeout(() => {
isScanning.value = true;
error.value = '';
}, 2000);
}
}
function onError(err) {
error.value = `[${err.name}]: `;
if (err.name === 'NotAllowedError') {
error.value += 'Necesitas otorgar permiso de acceso a la cámara';
} else if (err.name === 'NotFoundError') {
error.value += 'No se encontró cámara en este dispositivo';
} else if (err.name === 'NotSupportedError') {
error.value += 'Se requiere contexto seguro';
} else if (err.name === 'NotReadableError') {
error.value += '¿La cámara ya está en uso?';
} else if (err.name === 'OverconstrainedError') {
error.value += 'Las cámaras instaladas no son adecuadas';
} else if (err.name === 'StreamApiNotSupportedError') {
error.value += 'No es compatible con este navegador';
} else if (err.name === 'InsecureContextError') {
error.value += 'El acceso a la cámara solo se permite en contexto seguro.';
} else {
error.value += err.message;
}
console.error('Error de cámara:', err);
}
</script>
<template>
<div class="relative w-full h-full">
<!-- Componente de escaneo QR -->
<QrcodeStream
v-if="isScanning"
@detect="onDetect"
@error="onError"
:track="paintBoundingBox"
class="w-full h-full rounded-lg overflow-hidden"
>
</QrcodeStream>
<!-- Mensaje de éxito -->
<div
v-else
class="w-full h-full rounded-lg bg-green-600 flex items-center justify-center"
>
<div class="text-center text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-16 w-16 mx-auto mb-2"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<p class="text-lg font-semibold">¡Código detectado!</p>
</div>
</div>
<!-- Mensaje de error -->
<div
v-if="error"
class="absolute bottom-0 left-0 right-0 bg-red-500 text-white p-3 text-sm"
>
{{ error }}
</div>
</div>
</template>
<style scoped>
:deep(video) {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>