ADD: Integración del escáner QR y actualización de dependencias
This commit is contained in:
parent
98b55de050
commit
5204bfba24
64
package-lock.json
generated
64
package-lock.json
generated
@ -23,6 +23,7 @@
|
|||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "^11.1.1",
|
"vue-i18n": "^11.1.1",
|
||||||
"vue-multiselect": "^3.2.0",
|
"vue-multiselect": "^3.2.0",
|
||||||
|
"vue-qrcode-reader": "^5.7.3",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"ziggy-js": "^2.5.2"
|
"ziggy-js": "^2.5.2"
|
||||||
},
|
},
|
||||||
@ -1224,6 +1225,18 @@
|
|||||||
"vite": "^5.2.0 || ^6"
|
"vite": "^5.2.0 || ^6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/dom-webcodecs": {
|
||||||
|
"version": "0.1.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.17.tgz",
|
||||||
|
"integrity": "sha512-IwKW5uKL0Zrv5ccUJpjIlqf7ppk2v29l/ZLQxLlwHxljBfnDD9Gxm+hzMkGM0AOAL/21H0pp7cTUYLiiVUGchA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/emscripten": {
|
||||||
|
"version": "1.41.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.41.5.tgz",
|
||||||
|
"integrity": "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||||
@ -1492,6 +1505,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/barcode-detector": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-JcSekql+EV93evfzF9zBr+Y6aRfkR+QFvgyzbwQ0dbymZXoAI9+WgT7H1E429f+3RKNncHz2CW98VQtaaKpmfQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/dom-webcodecs": "^0.1.11",
|
||||||
|
"zxing-wasm": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/birpc": {
|
"node_modules/birpc": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz",
|
||||||
@ -3230,6 +3253,12 @@
|
|||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sdp": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-lwsAIzOPlH8/7IIjjz3K0zYBk7aBVVcvjMwt3M4fLxpjMYyy7i3I97SLHebgn4YBjirkzfp3RvRDWSKsh/+WFw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/socket.io-client": {
|
"node_modules/socket.io-client": {
|
||||||
"version": "4.8.1",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||||
@ -3641,6 +3670,19 @@
|
|||||||
"npm": ">= 6.14.15"
|
"npm": ">= 6.14.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-qrcode-reader": {
|
||||||
|
"version": "5.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-5.7.3.tgz",
|
||||||
|
"integrity": "sha512-iSGko42FsEvdHyizBMBs/X+HMO9Z5ONDxjW+mQdoraOR5emRNedmjC5SEJdYzGz8ZP5ME3lwB4iHy3S7MOt5Qw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"barcode-detector": "2.2.2",
|
||||||
|
"webrtc-adapter": "8.2.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "4.5.1",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
|
||||||
@ -3662,6 +3704,19 @@
|
|||||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/webrtc-adapter": {
|
||||||
|
"version": "8.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz",
|
||||||
|
"integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"sdp": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0",
|
||||||
|
"npm": ">=3.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.17.1",
|
"version": "8.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
@ -3711,6 +3766,15 @@
|
|||||||
"@types/qs": "^6.9.17",
|
"@types/qs": "^6.9.17",
|
||||||
"qs": "~6.9.7"
|
"qs": "~6.9.7"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zxing-wasm": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-MYm9k/5YVs4ZOTIFwlRjfFKD0crhefgbnt1+6TEpmKUDFp3E2uwqGSKwQOd2hOIsta/7Usq4hnpNRYTLoljnfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/emscripten": "^1.39.10"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "^11.1.1",
|
"vue-i18n": "^11.1.1",
|
||||||
"vue-multiselect": "^3.2.0",
|
"vue-multiselect": "^3.2.0",
|
||||||
|
"vue-qrcode-reader": "^5.7.3",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"ziggy-js": "^2.5.2"
|
"ziggy-js": "^2.5.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
import Input from "@Holos/Form/Input.vue";
|
import Input from "@Holos/Form/Input.vue";
|
||||||
|
import QRscan from "./QRscan.vue";
|
||||||
|
|
||||||
/** Eventos */
|
/** Eventos */
|
||||||
const emit = defineEmits(["fine-searched"]);
|
const emit = defineEmits(["fine-searched"]);
|
||||||
@ -64,26 +65,10 @@ const handlePayment = () => {
|
|||||||
Escanear Código QR
|
Escanear Código QR
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
class="w-full h-64 bg-gray-800 rounded-lg flex flex-col items-center justify-center"
|
class="w-full h-1/2 bg-gray-800 rounded-lg flex flex-col items-center justify-center"
|
||||||
>
|
>
|
||||||
<!-- Icono de QR -->
|
<!-- QR -->
|
||||||
<div class="text-gray-500 mb-3">
|
<QRscan v-model:fineNumber="fineNumber" @fine-searched="handleSearch" />
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-16 w-16"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<p class="text-gray-500 text-sm">Simulador de cámara QR</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
130
src/components/App/QRscan.vue
Normal file
130
src/components/App/QRscan.vue
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { QrcodeStream } from 'vue-qrcode-reader';
|
||||||
|
|
||||||
|
/** Props y Emits */
|
||||||
|
defineProps({
|
||||||
|
fineNumber: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:fineNumber', 'fine-searched']);
|
||||||
|
|
||||||
|
/** 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 = 2;
|
||||||
|
ctx.strokeStyle = '#7a0b3a'; // Color del tema
|
||||||
|
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:fineNumber', code);
|
||||||
|
|
||||||
|
// Pausar el escaneo después de detectar
|
||||||
|
isScanning.value = false;
|
||||||
|
|
||||||
|
// Emitir evento para buscar la multa
|
||||||
|
emit('fine-searched');
|
||||||
|
|
||||||
|
// 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user