Files
myprojplanet_vite/src/components/CQRCode/CQRCode.ts
Surya Paolo 6d78f82099 - aggiornamento di tante cose...
- generazione Volantini
- pagina RIS
2025-12-17 10:07:42 +01:00

415 lines
10 KiB
TypeScript
Executable File

import {
computed,
defineComponent,
onMounted,
ref,
toRefs,
reactive,
nextTick,
} from 'vue';
import { tools } from '@tools';
import { costanti } from '@costanti';
import { useGlobalStore } from '@store/globalStore';
import { useUserStore } from '@store/UserStore';
import { useI18n } from 'vue-i18n';
import { toolsext } from '@store/Modules/toolsext';
import { useQuasar } from 'quasar';
import { useRouter } from 'vue-router';
// Import per lettura QR
import { QrStream, QrCapture, QrDropzone } from 'vue3-qr-reader';
// Import per generazione QR
import QRCodeVue3 from 'qrcode-vue3';
export default defineComponent({
name: 'CQRCode',
components: {
QrStream,
QrCapture,
QrDropzone,
QRCodeVue3,
},
props: {
link: {
type: String,
required: false,
default: '',
},
textlink: {
type: String,
required: false,
default: '',
},
imglogo: {
type: String,
required: false,
default: '',
},
read: {
type: Boolean,
required: false,
default: false,
},
size: {
type: Number,
required: false,
default: 250,
},
primaryColor: {
type: String,
required: false,
default: '#26249a',
},
},
emits: ['decoded', 'error'],
setup(props, { emit }) {
const { t } = useI18n();
const $q = useQuasar();
const globalStore = useGlobalStore();
const userStore = useUserStore();
const $router = useRouter();
const qrDisplayRef = ref<HTMLElement | null>(null);
// State
const state = reactive({
data: null as string | null,
isDownloading: false,
uploadedFile: null as File | null,
});
// Computed
const qrSize = computed(() => {
// Responsive size
if ($q.screen.lt.sm) {
return Math.min(props.size, window.innerWidth - 80);
}
return props.size;
});
const logoImage = computed(() => {
return props.imglogo || tools.getimglogo();
});
const dotsOptions = computed(() => ({
type: 'rounded' as const,
color: props.primaryColor,
gradient: {
type: 'linear' as const,
rotation: 0,
colorStops: [
{ offset: 0, color: props.primaryColor },
{ offset: 1, color: lightenColor(props.primaryColor, 20) },
],
},
}));
const canShare = computed(() => {
return !!navigator.share;
});
const downloadFilename = computed(() => {
const username = userStore.my?.username || 'user';
const timestamp = Date.now();
return `qrcode-${username}-${timestamp}`;
});
// Methods
function onDecode(data: string) {
if (data) {
state.data = data;
emit('decoded', data);
// Vibrazione feedback su mobile
if (navigator.vibrate) {
navigator.vibrate(100);
}
$q.notify({
type: 'positive',
message: 'QR Code rilevato!',
position: 'top',
timeout: 2000,
});
}
}
function handleFileUpload(file: File | null) {
if (!file) return;
// Qui potresti usare una libreria per decodificare QR da immagine
// Per esempio: jsQR
$q.notify({
type: 'info',
message: 'Analisi immagine in corso...',
position: 'top',
});
}
async function findCanvas(maxAttempts = 10): Promise<HTMLCanvasElement | null> {
for (let i = 0; i < maxAttempts; i++) {
// Cerca in vari modi
let canvas: HTMLCanvasElement | null = null;
// Metodo 1: cerca nel ref
if (qrDisplayRef.value) {
canvas = qrDisplayRef.value.querySelector('canvas');
}
// Metodo 2: cerca con classe specifica
if (!canvas) {
const qrCode = document.querySelector('.qr-code');
canvas = qrCode?.querySelector('canvas') || null;
}
// Metodo 3: cerca nel container .qr-display
if (!canvas) {
const container = document.querySelector('.qr-display');
canvas = container?.querySelector('canvas') || null;
}
if (canvas && canvas.width > 0 && canvas.height > 0) {
return canvas;
}
// Aspetta prima del prossimo tentativo
await new Promise((resolve) => setTimeout(resolve, 100));
}
return null;
}
// Nuova funzione downloadQR
async function downloadQR() {
state.isDownloading = true;
try {
await nextTick();
await new Promise((resolve) => setTimeout(resolve, 200));
let dataUrl: string | null = null;
// Metodo 1: Cerca l'immagine (QRCodeVue3 genera un <img> con base64)
const qrDisplay = qrDisplayRef.value || document.querySelector('.qr-display');
if (qrDisplay) {
const img = qrDisplay.querySelector('img') as HTMLImageElement;
if (img && img.src && img.src.startsWith('data:image')) {
console.log('✅ Immagine base64 trovata!');
dataUrl = img.src;
}
}
// Metodo 2: Se non trova img, cerca canvas (fallback)
if (!dataUrl) {
const canvas = document.querySelector(
'.qr-display canvas'
) as HTMLCanvasElement;
if (canvas && canvas.width > 0) {
console.log('✅ Canvas trovato come fallback');
dataUrl = canvas.toDataURL('image/png');
}
}
if (!dataUrl) {
debugCanvas();
throw new Error('QR Code non trovato. Riprova.');
}
// Download
const link = document.createElement('a');
link.href = dataUrl;
link.download = `${downloadFilename.value}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
$q.notify({
type: 'positive',
message: 'QR Code scaricato!',
icon: 'download_done',
position: 'top',
});
} catch (error: any) {
console.error('Errore download QR:', error);
$q.notify({
type: 'negative',
message: error.message || 'Errore durante il download',
position: 'top',
});
emit('error', error);
} finally {
state.isDownloading = false;
}
}
async function shareQR() {
try {
// Cerca l'immagine base64
const qrDisplay = qrDisplayRef.value || document.querySelector('.qr-display');
const img = qrDisplay?.querySelector('img') as HTMLImageElement;
if (img && img.src && img.src.startsWith('data:image')) {
// Converti base64 in blob
const response = await fetch(img.src);
const blob = await response.blob();
const file = new File([blob], `${downloadFilename.value}.png`, {
type: 'image/png',
});
if (navigator.canShare && navigator.canShare({ files: [file] })) {
await navigator.share({
title: props.textlink || 'QR Code',
files: [file],
});
$q.notify({
type: 'positive',
message: 'Condivisione avviata!',
position: 'top',
});
return;
}
}
// Fallback: condividi solo il link
await navigator.share({
title: props.textlink || 'QR Code',
url: props.link,
});
} catch (error: any) {
if (error.name !== 'AbortError') {
console.error('Errore condivisione:', error);
}
}
}
function copyToClipboard(text: string) {
navigator.clipboard
.writeText(text)
.then(() => {
$q.notify({
type: 'positive',
message: 'Copiato negli appunti!',
icon: 'content_copy',
position: 'top',
timeout: 1500,
});
})
.catch((err) => {
console.error('Errore copia:', err);
});
}
function isValidUrl(text: string): boolean {
try {
new URL(text);
return true;
} catch {
return false;
}
}
function truncateUrl(url: string, maxLength: number = 400): string {
if (!url || url.length <= maxLength) return url;
return url.substring(0, maxLength) + '...';
}
function lightenColor(color: string, percent: number): string {
const num = parseInt(color.replace('#', ''), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) + amt;
const G = ((num >> 8) & 0x00ff) + amt;
const B = (num & 0x0000ff) + amt;
return (
'#' +
(
0x1000000 +
(R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
(G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
(B < 255 ? (B < 1 ? 0 : B) : 255)
)
.toString(16)
.slice(1)
);
}
function naviga(path: string) {
$router.push(path);
}
function debugCanvas() {
console.log('=== DEBUG QR ===');
console.log('1. qrDisplayRef.value:', qrDisplayRef.value);
const qrDisplay = qrDisplayRef.value || document.querySelector('.qr-display');
if (qrDisplay) {
// Cerca immagini
const imgs = qrDisplay.querySelectorAll('img');
console.log('2. Immagini trovate:', imgs.length);
imgs.forEach((img, i) => {
const imgEl = img as HTMLImageElement;
console.log(
` Img ${i}: src starts with data:image = ${imgEl.src?.startsWith('data:image')}`
);
});
// Cerca canvas
const canvases = qrDisplay.querySelectorAll('canvas');
console.log('3. Canvas trovati:', canvases.length);
console.log(
'4. innerHTML (primi 200 char):',
qrDisplay.innerHTML.substring(0, 200)
);
}
}
// Lifecycle
onMounted(() => {
// Inizializzazione se necessaria
});
return {
// Stores
globalStore,
userStore,
// Utils
t,
tools,
costanti,
toolsext,
// State
...toRefs(state),
// Computed
qrSize,
logoImage,
dotsOptions,
canShare,
downloadFilename,
primaryColor: props.primaryColor,
// Methods
onDecode,
handleFileUpload,
qrDisplayRef,
downloadQR,
shareQR,
copyToClipboard,
isValidUrl,
truncateUrl,
naviga,
};
},
});