|
|
@@ -1,7 +1,5 @@
|
|
|
<script setup>
|
|
|
-import { onMounted, ref, watch } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
-import QRious from 'qrious';
|
|
|
import { CopyOutlined, DownloadOutlined } from '@ant-design/icons-vue';
|
|
|
import { message } from 'ant-design-vue';
|
|
|
|
|
|
@@ -9,73 +7,14 @@ import { ClipboardManager, FileManager } from '@/utils';
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
-// Renders a single share-link as a clickable QR code + a copy button
|
|
|
-// + (optional) a download button. Used per-link inside the inbound
|
|
|
-// info modal — the canvas is repainted whenever `value` changes.
|
|
|
-
|
|
|
const props = defineProps({
|
|
|
- // The link or config text to encode + display.
|
|
|
value: { type: String, required: true },
|
|
|
- // Header label shown next to the copy button.
|
|
|
remark: { type: String, default: '' },
|
|
|
- // Optional download filename — when set, surfaces a download button.
|
|
|
downloadName: { type: String, default: '' },
|
|
|
- // Final on-screen QR size in CSS pixels. The canvas drawing buffer
|
|
|
- // is rounded down to a multiple of the QR matrix width (so the QR
|
|
|
- // fills it edge-to-edge) and CSS then scales the canvas to exactly
|
|
|
- // this size — so a denser QR (e.g. WireGuard config) and a sparser
|
|
|
- // one (its link) display at identical dimensions.
|
|
|
size: { type: Number, default: 240 },
|
|
|
- // Toggle the QR rendering off when callers only want the "row of buttons"
|
|
|
- // styling (used when the legacy panel rendered links without QRs).
|
|
|
showQr: { type: Boolean, default: true },
|
|
|
});
|
|
|
|
|
|
-const canvas = ref(null);
|
|
|
-
|
|
|
-// Byte-mode capacities (level M) for QR versions 1..40 — used to pick
|
|
|
-// the matrix width up front so we can size the canvas as a multiple
|
|
|
-// of pixelSize. Without this, QRious renders at floor(size/matrix)
|
|
|
-// and centers, leaving a white margin whenever size isn't divisible.
|
|
|
-const QR_M_BYTE_CAPACITY = [
|
|
|
- 14, 26, 42, 62, 84, 106, 122, 152, 180, 213,
|
|
|
- 251, 287, 331, 362, 412, 450, 504, 560, 624, 666,
|
|
|
- 711, 779, 857, 911, 997, 1059, 1125, 1190, 1264, 1370,
|
|
|
- 1452, 1538, 1628, 1722, 1809, 1911, 1989, 2099, 2213, 2331,
|
|
|
-];
|
|
|
-
|
|
|
-function pickQrMatrixWidth(value) {
|
|
|
- const byteLen = new TextEncoder().encode(value).length;
|
|
|
- for (let i = 0; i < QR_M_BYTE_CAPACITY.length; i++) {
|
|
|
- if (byteLen <= QR_M_BYTE_CAPACITY[i]) return 17 + 4 * (i + 1);
|
|
|
- }
|
|
|
- return 17 + 4 * 40; // version 40 (177 modules)
|
|
|
-}
|
|
|
-
|
|
|
-function paint() {
|
|
|
- if (!props.showQr || !canvas.value || !props.value) return;
|
|
|
- // Canvas size = matrixWidth × pixelSize, so the QR fills it edge-to-
|
|
|
- // edge. pixelSize is floored against the requested size so the QR
|
|
|
- // never grows past the host's expected box.
|
|
|
- const matrixWidth = pickQrMatrixWidth(props.value);
|
|
|
- const pixelSize = Math.max(1, Math.floor(props.size / matrixWidth));
|
|
|
- const exactSize = matrixWidth * pixelSize;
|
|
|
- new QRious({
|
|
|
- element: canvas.value,
|
|
|
- size: exactSize,
|
|
|
- value: props.value,
|
|
|
- background: 'white',
|
|
|
- backgroundAlpha: 1,
|
|
|
- foreground: 'black',
|
|
|
- padding: 0,
|
|
|
- level: 'M',
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-onMounted(paint);
|
|
|
-watch(() => props.value, paint);
|
|
|
-watch(() => props.size, paint);
|
|
|
-
|
|
|
async function copy() {
|
|
|
const ok = await ClipboardManager.copyText(props.value);
|
|
|
if (ok) message.success(t('copied'));
|
|
|
@@ -107,7 +46,8 @@ function download() {
|
|
|
</a-tooltip>
|
|
|
</div>
|
|
|
<div v-if="showQr" class="qr-panel-canvas">
|
|
|
- <canvas ref="canvas" :style="{ width: `${size}px`, height: `${size}px` }" @click="copy" />
|
|
|
+ <a-qrcode class="qr-code" :value="value" :size="size" type="svg" :bordered="false"
|
|
|
+ :title="t('copy')" @click="copy" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -140,14 +80,10 @@ function download() {
|
|
|
padding: 6px 0;
|
|
|
}
|
|
|
|
|
|
-.qr-panel-canvas canvas {
|
|
|
+.qr-panel-canvas .qr-code {
|
|
|
cursor: pointer;
|
|
|
- display: block;
|
|
|
+ padding: 0 !important;
|
|
|
+ background: #fff;
|
|
|
border-radius: 4px;
|
|
|
- /* Drawing buffer is matrix-snapped (smaller than display size for
|
|
|
- * dense QRs); scale up crisply so dense and sparse QRs share the
|
|
|
- * same on-screen footprint without blurring. */
|
|
|
- image-rendering: pixelated;
|
|
|
- image-rendering: crisp-edges;
|
|
|
}
|
|
|
</style>
|