feat: click QR to copy/save image instead of link text

This commit is contained in:
MHSanaei
2026-05-14 17:40:40 +02:00
parent 7065d41be6
commit e4218a1029

View File

@@ -1,6 +1,7 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { CopyOutlined, DownloadOutlined } from '@ant-design/icons-vue';
import { CopyOutlined, DownloadOutlined, PictureOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { ClipboardManager, FileManager } from '@/utils';
@@ -15,6 +16,8 @@ const props = defineProps({
showQr: { type: Boolean, default: true },
});
const qrRef = ref(null);
async function copy() {
const ok = await ClipboardManager.copyText(props.value);
if (ok) message.success(t('copied'));
@@ -24,6 +27,55 @@ function download() {
if (!props.downloadName) return;
FileManager.downloadTextFile(props.value, props.downloadName);
}
function svgToPngBlob(size = 360) {
const svgEl = qrRef.value?.querySelector('svg');
if (!svgEl) return Promise.resolve(null);
const svgData = new XMLSerializer().serializeToString(svgEl);
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(svgBlob);
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, size, size);
ctx.drawImage(img, 0, 0, size, size);
URL.revokeObjectURL(url);
canvas.toBlob(resolve, 'image/png');
};
img.onerror = () => { URL.revokeObjectURL(url); resolve(null); };
img.src = url;
});
}
async function copyImage() {
const blob = await svgToPngBlob(props.size);
if (!blob) return;
try {
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
message.success(t('copied'));
} catch {
downloadImageBlob(blob);
}
}
function downloadImageBlob(blob) {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${props.remark || 'qrcode'}.png`;
link.click();
URL.revokeObjectURL(url);
}
async function downloadImage() {
const blob = await svgToPngBlob(props.size);
if (blob) downloadImageBlob(blob);
}
</script>
<template>
@@ -37,6 +89,13 @@ function download() {
</template>
</a-button>
</a-tooltip>
<a-tooltip v-if="showQr" :title="t('downloadImage', 'Download Image')">
<a-button size="small" @click="downloadImage">
<template #icon>
<PictureOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip v-if="downloadName" :title="t('download')">
<a-button size="small" @click="download">
<template #icon>
@@ -45,9 +104,11 @@ function download() {
</a-button>
</a-tooltip>
</div>
<div v-if="showQr" class="qr-panel-canvas">
<a-qrcode class="qr-code" :value="value" :size="size" type="svg" :bordered="false"
color="#000000" bg-color="#ffffff" :title="t('copy')" @click="copy" />
<div v-if="showQr" ref="qrRef" class="qr-panel-canvas">
<a-tooltip :title="t('copy')">
<a-qrcode class="qr-code" :value="value" :size="size" type="svg" :bordered="false" color="#000000"
bg-color="#ffffff" @click="copyImage" />
</a-tooltip>
</div>
</div>
</template>