189 lines
3.8 KiB
Vue
189 lines
3.8 KiB
Vue
<script lang="ts" setup>
|
|
import QRCode from "qrcode";
|
|
|
|
const props = defineProps({
|
|
title: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
qrCodeContent: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
qrCodeSize: {
|
|
type: Number,
|
|
default: 200,
|
|
},
|
|
showLabel: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
label: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
});
|
|
|
|
const visible = defineModel<boolean>("visible");
|
|
const qrCodeDataUrl = ref("");
|
|
const loading = ref(false);
|
|
|
|
const generateQrCode = async () => {
|
|
if (!props.qrCodeContent) {
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
try {
|
|
qrCodeDataUrl.value = await QRCode.toDataURL(props.qrCodeContent, {
|
|
width: props.qrCodeSize,
|
|
margin: 2,
|
|
color: {
|
|
dark: "#000000",
|
|
light: "#ffffff",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Generate QR code failed:", error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
const handlePrint = () => {
|
|
const printWindow = window.open("", "_blank");
|
|
if (!printWindow) {
|
|
return;
|
|
}
|
|
|
|
const imgHtml = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>${props.title || "QR Code"}</title>
|
|
<style>
|
|
body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
img {
|
|
max-width: 100%;
|
|
}
|
|
.label {
|
|
margin-top: 10px;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
}
|
|
@media print {
|
|
body {
|
|
padding: 0;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<img src="${qrCodeDataUrl.value}" alt="QR Code" />
|
|
${props.showLabel ? `<div class="label">${props.label || props.qrCodeContent}</div>` : ""}
|
|
</body>
|
|
</html>
|
|
`;
|
|
|
|
printWindow.document.write(imgHtml);
|
|
printWindow.document.close();
|
|
printWindow.focus();
|
|
setTimeout(() => {
|
|
printWindow.print();
|
|
printWindow.close();
|
|
}, 250);
|
|
};
|
|
|
|
const handleDownload = () => {
|
|
const link = document.createElement("a");
|
|
link.download = `${props.label || props.qrCodeContent || "qrcode"}.png`;
|
|
link.href = qrCodeDataUrl.value;
|
|
link.click();
|
|
};
|
|
|
|
watch(
|
|
() => props.qrCodeContent,
|
|
() => {
|
|
if (visible.value && props.qrCodeContent) {
|
|
generateQrCode();
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(visible, newVal => {
|
|
if (newVal && props.qrCodeContent) {
|
|
generateQrCode();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<el-dialog
|
|
:title="title"
|
|
v-model="visible"
|
|
width="400px"
|
|
:close-on-click-modal="false"
|
|
:close-on-press-escape="false"
|
|
>
|
|
<div v-loading="loading" class="qrcode-container">
|
|
<div v-if="qrCodeDataUrl" class="qrcode-display">
|
|
<img :src="qrCodeDataUrl" alt="QR Code" />
|
|
<div v-if="showLabel" class="qrcode-label">{{ label || qrCodeContent }}</div>
|
|
</div>
|
|
<div v-else class="qrcode-placeholder">
|
|
{{ $t("_message.warehouse.warehouse_item.no_qrcode_content") }}
|
|
</div>
|
|
</div>
|
|
<template #footer>
|
|
<el-button @click="visible = false">{{ $t("_button.cancel") }}</el-button>
|
|
<el-button type="primary" @click="handleDownload" :disabled="!qrCodeDataUrl">
|
|
{{ $t("_button.download") }}
|
|
</el-button>
|
|
<el-button type="success" @click="handlePrint" :disabled="!qrCodeDataUrl">
|
|
{{ $t("_button.print") }}
|
|
</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.qrcode-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 200px;
|
|
}
|
|
|
|
.qrcode-display {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.qrcode-display img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
.qrcode-label {
|
|
margin-top: 10px;
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.qrcode-placeholder {
|
|
font-size: 14px;
|
|
color: #909399;
|
|
}
|
|
</style>
|