You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

169 lines
4.7 KiB

<!-- QRScannerDialog.vue -->
<template>
<div
v-if="visible && !isNativePlatform"
class="dialog-overlay z-[60] fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
>
<div
class="dialog relative bg-white rounded-lg shadow-xl max-w-lg w-full mx-4"
>
<!-- Header -->
<div class="p-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">Scan QR Code</h3>
<button
class="absolute top-4 right-4 text-gray-400 hover:text-gray-500"
aria-label="Close dialog"
@click="close"
>
<svg
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<!-- Scanner -->
<div class="p-4">
<div
v-if="useQRReader && !isNativePlatform"
class="relative aspect-square"
>
<qrcode-stream
:camera="options?.camera === 'front' ? 'user' : 'environment'"
@decode="onDecode"
@init="onInit"
/>
<div
class="absolute inset-0 border-2 border-blue-500 opacity-50 pointer-events-none"
></div>
</div>
<div v-else class="text-center py-8">
<p class="text-gray-500">
{{
isNativePlatform
? "Using native camera scanner..."
: "QR code scanning is not supported in this browser."
}}
</p>
</div>
</div>
<!-- Footer -->
<div class="p-4 border-t border-gray-200">
<p v-if="error" class="text-red-500 text-sm mb-4">{{ error }}</p>
<div class="flex justify-end">
<button
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200"
@click="close"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-facing-decorator";
import { QrcodeStream } from "vue-qrcode-reader";
import { QRScannerOptions } from "@/services/QRScanner/types";
import { logger } from "@/utils/logger";
import { Capacitor } from "@capacitor/core";
@Component({
components: {
QrcodeStream,
},
})
export default class QRScannerDialog extends Vue {
@Prop({ type: Function, required: true }) onScan!: (result: string) => void;
@Prop({ type: Function }) onError?: (error: Error) => void;
@Prop({ type: Object }) options?: QRScannerOptions;
visible = true;
error: string | null = null;
useQRReader = __USE_QR_READER__;
isNativePlatform =
Capacitor.isNativePlatform() ||
__IS_MOBILE__ ||
Capacitor.getPlatform() === "android" ||
Capacitor.getPlatform() === "ios";
created() {
logger.log("QRScannerDialog platform detection:", {
capacitorNative: Capacitor.isNativePlatform(),
isMobile: __IS_MOBILE__,
platform: Capacitor.getPlatform(),
useQRReader: this.useQRReader,
isNativePlatform: this.isNativePlatform,
});
// If on native platform, close immediately and don't initialize web scanner
if (this.isNativePlatform) {
logger.log("Closing QR dialog on native platform");
this.$nextTick(() => this.close());
}
}
async onInit(promise: Promise<void>): Promise<void> {
// Don't initialize on mobile platforms
if (this.isNativePlatform) {
logger.log("Skipping web scanner initialization on native platform");
return;
}
try {
await promise;
this.error = null;
} catch (error) {
this.error = error instanceof Error ? error.message : String(error);
if (this.onError) {
this.onError(error instanceof Error ? error : new Error(String(error)));
}
logger.error("Error initializing QR scanner:", error);
}
}
onDecode(result: string): void {
try {
this.onScan(result);
this.close();
} catch (error) {
this.error = error instanceof Error ? error.message : String(error);
if (this.onError) {
this.onError(error instanceof Error ? error : new Error(String(error)));
}
logger.error("Error handling QR scan result:", error);
}
}
async close(): Promise<void> {
this.visible = false;
await this.$nextTick();
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
}
}
</script>
<style scoped>
.dialog-overlay {
backdrop-filter: blur(4px);
}
.qrcode-stream {
width: 100%;
height: 100%;
}
</style>