130 lines
3.2 KiB
Vue
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> |