mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 05:47:10 -04:00
fix(QR Code Generator): more styling, SVG, image upload
- Use pp-qr-code-styling to allow image upload, SVG and more styling options (except gradient) - Emit Console/Terminal Escape QRCode Fix #621 and #592
This commit is contained in:
parent
e876d03608
commit
f2e00226f8
8 changed files with 455 additions and 58 deletions
|
@ -1,8 +1,12 @@
|
|||
import { extension as getExtensionFromMime } from 'mime-types';
|
||||
import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types';
|
||||
import type { Ref } from 'vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
export { getMimeTypeFromBase64, useDownloadFileFromBase64 };
|
||||
export {
|
||||
getMimeTypeFromBase64,
|
||||
getMimeTypeFromExtension, getExtensionFromMimeType,
|
||||
useDownloadFileFromBase64, useDownloadFileFromBase64Refs,
|
||||
};
|
||||
|
||||
const commonMimeTypesSignatures = {
|
||||
'JVBERi0': 'application/pdf',
|
||||
|
@ -36,30 +40,55 @@ function getFileExtensionFromMimeType({
|
|||
defaultExtension?: string
|
||||
}) {
|
||||
if (mimeType) {
|
||||
return getExtensionFromMime(mimeType) ?? defaultExtension;
|
||||
return getExtensionFromMimeType(mimeType) ?? defaultExtension;
|
||||
}
|
||||
|
||||
return defaultExtension;
|
||||
}
|
||||
|
||||
function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
|
||||
function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }:
|
||||
{ sourceValue: string; filename?: string; extension?: string; fileMimeType?: string }) {
|
||||
if (sourceValue === '') {
|
||||
throw new Error('Base64 string is empty');
|
||||
}
|
||||
|
||||
const defaultExtension = extension ?? 'txt';
|
||||
const { mimeType } = getMimeTypeFromBase64({ base64String: sourceValue });
|
||||
let base64String = sourceValue;
|
||||
if (!mimeType) {
|
||||
const targetMimeType = fileMimeType ?? getMimeTypeFromExtension(defaultExtension);
|
||||
base64String = `data:${targetMimeType};base64,${sourceValue}`;
|
||||
}
|
||||
|
||||
const cleanExtension = extension ?? getFileExtensionFromMimeType(
|
||||
{ mimeType, defaultExtension });
|
||||
let cleanFileName = filename ?? `file.${cleanExtension}`;
|
||||
if (extension && !cleanFileName.endsWith(`.${extension}`)) {
|
||||
cleanFileName = `${cleanFileName}.${cleanExtension}`;
|
||||
}
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = base64String;
|
||||
a.download = cleanFileName;
|
||||
a.click();
|
||||
}
|
||||
|
||||
function useDownloadFileFromBase64(
|
||||
{ source, filename, extension, fileMimeType }:
|
||||
{ source: Ref<string>; filename?: string; extension?: string; fileMimeType?: string }) {
|
||||
return {
|
||||
download() {
|
||||
if (source.value === '') {
|
||||
throw new Error('Base64 string is empty');
|
||||
}
|
||||
|
||||
const { mimeType } = getMimeTypeFromBase64({ base64String: source.value });
|
||||
const base64String = mimeType
|
||||
? source.value
|
||||
: `data:text/plain;base64,${source.value}`;
|
||||
|
||||
const cleanFileName = filename ?? `file.${getFileExtensionFromMimeType({ mimeType })}`;
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = base64String;
|
||||
a.download = cleanFileName;
|
||||
a.click();
|
||||
downloadFromBase64({ sourceValue: source.value, filename, extension, fileMimeType });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function useDownloadFileFromBase64Refs(
|
||||
{ source, filename, extension }:
|
||||
{ source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) {
|
||||
return {
|
||||
download() {
|
||||
downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useRouteQuery } from '@vueuse/router';
|
||||
import { computed } from 'vue';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
|
||||
export { useQueryParam };
|
||||
export { useQueryParam, useQueryParamOrStorage };
|
||||
|
||||
const transformers = {
|
||||
number: {
|
||||
|
@ -33,3 +34,31 @@ function useQueryParam<T>({ name, defaultValue }: { name: string; defaultValue:
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
function useQueryParamOrStorage<T>({ name, storageName, defaultValue }: { name: string; storageName: string; defaultValue?: T }) {
|
||||
const type = typeof defaultValue;
|
||||
const transformer = transformers[type as keyof typeof transformers] ?? transformers.string;
|
||||
|
||||
const storageRef = useStorage(storageName, defaultValue);
|
||||
const storageDefaultValue = storageRef.value ?? defaultValue;
|
||||
|
||||
const proxy = useRouteQuery(name, transformer.toQuery(storageDefaultValue as never));
|
||||
|
||||
const ref = computed<T>({
|
||||
get() {
|
||||
return transformer.fromQuery(proxy.value) as unknown as T;
|
||||
},
|
||||
set(value) {
|
||||
proxy.value = transformer.toQuery(value as never);
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
ref,
|
||||
(newValue) => {
|
||||
storageRef.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,89 @@
|
|||
<script setup lang="ts">
|
||||
import type { QRCodeErrorCorrectionLevel } from 'qrcode';
|
||||
import { useQRCode } from './useQRCode';
|
||||
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
|
||||
import type {
|
||||
CornerDotType,
|
||||
CornerSquareType,
|
||||
DotType,
|
||||
ErrorCorrectionLevel,
|
||||
FileExtension,
|
||||
} from 'pp-qr-code';
|
||||
import qrcodeConsole from 'qrcode-terminal-nooctal';
|
||||
import { useQRCodeStyling } from './useQRCode';
|
||||
import { useDownloadFileFromBase64Refs } from '@/composable/downloadBase64';
|
||||
import { useQueryParamOrStorage } from '@/composable/queryParams';
|
||||
|
||||
const foreground = ref('#000000ff');
|
||||
const background = ref('#ffffffff');
|
||||
const errorCorrectionLevel = ref<QRCodeErrorCorrectionLevel>('medium');
|
||||
const foreground = useQueryParamOrStorage({ name: 'fg', storageName: 'qr-code-gen:fg', defaultValue: '#000000ff' });
|
||||
const background = useQueryParamOrStorage({ name: 'bg', storageName: 'qr-code-gen:bg', defaultValue: '#ffffffff' });
|
||||
const errorCorrectionLevelSelectValue = useQueryParamOrStorage<string>({ name: 'level', storageName: 'qr-code-gen:level', defaultValue: 'medium' });
|
||||
const errorCorrectionLevel = computed(() => errorCorrectionLevelSelectValue.value.toString()[0].toUpperCase() as ErrorCorrectionLevel);
|
||||
const width = useQueryParamOrStorage({ name: 'width', storageName: 'qr-code-gen:width', defaultValue: 1024 });
|
||||
const margin = useQueryParamOrStorage({ name: 'margin', storageName: 'qr-code-gen:margin', defaultValue: 10 });
|
||||
const imageSize = useQueryParamOrStorage({ name: 'imgsize', storageName: 'qr-code-gen:imsz', defaultValue: 0.4 });
|
||||
const imageMargin = useQueryParamOrStorage({ name: 'imgmargin', storageName: 'qr-code-gen:immg', defaultValue: 20 });
|
||||
const outputType = useQueryParamOrStorage<FileExtension>({ name: 'out', storageName: 'qr-code-gen:out', defaultValue: 'png' });
|
||||
const dotType = useQueryParamOrStorage<DotType>({ name: 'dot', storageName: 'qr-code-gen:dot', defaultValue: 'square' });
|
||||
const dotColor = useQueryParamOrStorage<string>({ name: 'dotc', storageName: 'qr-code-gen:dotc', defaultValue: '#ffffffff' });
|
||||
const cornersDotType = useQueryParamOrStorage<CornerDotType>({ name: 'cdt', storageName: 'qr-code-gen:cdt', defaultValue: 'square' });
|
||||
const cornersDotColor = useQueryParamOrStorage<string>({ name: 'cdtc', storageName: 'qr-code-gen:cdtc', defaultValue: '#ffffffff' });
|
||||
const cornersSquareType = useQueryParamOrStorage<CornerSquareType>({ name: 'cst', storageName: 'qr-code-gen:cst', defaultValue: 'square' });
|
||||
const cornersSquareColor = useQueryParamOrStorage<string>({ name: 'cstc', storageName: 'qr-code-gen:cstc', defaultValue: '#ffffffff' });
|
||||
const smallTerminal = useQueryParamOrStorage<boolean>({ name: 'sml', storageName: 'qr-code-gen:sml', defaultValue: false });
|
||||
const fileInput = ref() as Ref<File>;
|
||||
const { base64: imageBase64 } = useBase64(fileInput);
|
||||
async function onUpload(file: File) {
|
||||
if (file) {
|
||||
fileInput.value = file;
|
||||
}
|
||||
}
|
||||
|
||||
const errorCorrectionLevels = ['low', 'medium', 'quartile', 'high'];
|
||||
const outputTypes = ['svg', 'png', 'jpeg', 'webp'];
|
||||
const dotTypes = ['dots',
|
||||
'random-dots',
|
||||
'rounded',
|
||||
'vertical-lines',
|
||||
'horizontal-lines',
|
||||
'classy',
|
||||
'classy-rounded',
|
||||
'square',
|
||||
'extra-rounded'];
|
||||
const cornersDotTypes = ['dot', 'square', 'heart'];
|
||||
const cornersSquareTypes = ['dot', 'square', 'extra-rounded'];
|
||||
|
||||
const text = ref('https://it-tools.tech');
|
||||
const { qrcode } = useQRCode({
|
||||
const { qrcode } = useQRCodeStyling({
|
||||
text,
|
||||
color: {
|
||||
background,
|
||||
foreground,
|
||||
},
|
||||
color: { background, foreground },
|
||||
errorCorrectionLevel,
|
||||
options: { width: 1024 },
|
||||
imageBase64,
|
||||
imageOptions: { imageSize, margin: imageMargin },
|
||||
dotOptions: { type: dotType, color: dotColor },
|
||||
cornersSquareOptions: { type: cornersSquareType, color: cornersSquareColor },
|
||||
cornersDotOptions: { type: cornersDotType, color: cornersDotColor },
|
||||
outputType,
|
||||
width,
|
||||
margin,
|
||||
});
|
||||
|
||||
const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-code.png' });
|
||||
const qrcodeTerminal = computedAsync(() => {
|
||||
const textValue = text.value;
|
||||
const level = errorCorrectionLevel.value;
|
||||
const small = smallTerminal.value;
|
||||
return new Promise<string>((resolve, _reject) => {
|
||||
try {
|
||||
qrcodeConsole.setErrorLevel(level);
|
||||
qrcodeConsole.generate(textValue, { small }, (qrcode: string) => {
|
||||
resolve(qrcode);
|
||||
});
|
||||
}
|
||||
catch (_) {
|
||||
resolve('');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const filename = ref('qr-code');
|
||||
const extension = computed(() => outputType.value.toString());
|
||||
const { download } = useDownloadFileFromBase64Refs({ source: qrcode, filename, extension });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -46,8 +109,14 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
<n-form-item label="Background color:">
|
||||
<n-color-picker v-model:value="background" :modes="['hex']" />
|
||||
</n-form-item>
|
||||
<n-form-item label="Width:">
|
||||
<n-input-number v-model:value="width" :min="0" />
|
||||
</n-form-item>
|
||||
<n-form-item label="Margin:">
|
||||
<n-input-number v-model:value="margin" :min="0" />
|
||||
</n-form-item>
|
||||
<c-select
|
||||
v-model:value="errorCorrectionLevel"
|
||||
v-model:value="errorCorrectionLevelSelectValue"
|
||||
label="Error resistance:"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
|
@ -55,14 +124,104 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
|
|||
:options="errorCorrectionLevels.map((value) => ({ label: value, value }))"
|
||||
/>
|
||||
</n-form>
|
||||
<c-card title="Image" mt-3>
|
||||
<c-file-upload title="Drag and drop an image here, or click to select an image" @file-upload="onUpload" />
|
||||
|
||||
<n-form label-width="130" label-placement="left" mt-3>
|
||||
<n-form-item label="Size:">
|
||||
<n-input-number v-model:value="imageSize" :min="0" step="0.1" />
|
||||
</n-form-item>
|
||||
<n-form-item label="Margin:">
|
||||
<n-input-number v-model:value="imageMargin" :min="0" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</c-card>
|
||||
<c-card mt-3>
|
||||
<details>
|
||||
<summary>Dots Options</summary>
|
||||
<n-form label-width="130" label-placement="left">
|
||||
<n-form-item label="Color:">
|
||||
<n-color-picker v-model:value="dotColor" :modes="['hex']" />
|
||||
</n-form-item>
|
||||
<c-select
|
||||
v-model:value="dotType"
|
||||
label="Type:"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
:options="dotTypes.map((value) => ({ label: value, value }))"
|
||||
/>
|
||||
</n-form>
|
||||
</details>
|
||||
</c-card>
|
||||
<c-card mt-3>
|
||||
<details>
|
||||
<summary>Corners Dots Options</summary>
|
||||
<n-form label-width="130" label-placement="left">
|
||||
<n-form-item label="Color:">
|
||||
<n-color-picker v-model:value="cornersDotColor" :modes="['hex']" />
|
||||
</n-form-item>
|
||||
<c-select
|
||||
v-model:value="cornersDotType"
|
||||
label="Type:"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
:options="cornersDotTypes.map((value) => ({ label: value, value }))"
|
||||
/>
|
||||
</n-form>
|
||||
</details>
|
||||
</c-card>
|
||||
<c-card mt-3>
|
||||
<details>
|
||||
<summary>Corners Square Options</summary>
|
||||
<n-form label-width="130" label-placement="left">
|
||||
<n-form-item label="Color:">
|
||||
<n-color-picker v-model:value="cornersSquareColor" :modes="['hex']" />
|
||||
</n-form-item>
|
||||
<c-select
|
||||
v-model:value="cornersSquareType"
|
||||
label="Type:"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
:options="cornersSquareTypes.map((value) => ({ label: value, value }))"
|
||||
/>
|
||||
</n-form>
|
||||
</details>
|
||||
</c-card>
|
||||
<c-select
|
||||
v-model:value="outputType"
|
||||
mt-3
|
||||
label="Output format:"
|
||||
label-position="left"
|
||||
label-width="130px"
|
||||
label-align="right"
|
||||
:options="outputTypes.map((value) => ({ label: value.toUpperCase(), value }))"
|
||||
/>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<div flex flex-col items-center gap-3>
|
||||
<n-image :src="qrcode" width="200" />
|
||||
<c-button @click="download">
|
||||
Download qr-code
|
||||
Download qr-code ({{ outputType.toString().toUpperCase() }})
|
||||
</c-button>
|
||||
</div>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<n-checkbox v-model:checked="smallTerminal">
|
||||
Small Terminal
|
||||
</n-checkbox>
|
||||
<n-form-item label="Terminal output:" mt-1>
|
||||
<TextareaCopyable
|
||||
:value="qrcodeTerminal"
|
||||
multiline
|
||||
rows="5"
|
||||
mb-1 mt-1
|
||||
copy-placement="outside"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</c-card>
|
||||
|
|
5
src/tools/qr-code-generator/qrcode-terminal-nooctal.d.ts
vendored
Normal file
5
src/tools/qr-code-generator/qrcode-terminal-nooctal.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
declare module 'qrcode-terminal-nooctal' {
|
||||
export const error: 0 | 1 | 2 | 3;
|
||||
export function generate(input: string, opts?: { small: boolean }, callback?: (qrcode: string) => void): void;
|
||||
export function setErrorLevel(error: "L" | "M" | "Q" | "H"): void;
|
||||
}
|
|
@ -1,33 +1,182 @@
|
|||
import { type MaybeRef, get } from '@vueuse/core';
|
||||
import QRCode, { type QRCodeErrorCorrectionLevel, type QRCodeToDataURLOptions } from 'qrcode';
|
||||
import QRCode, { type QRCodeDataURLType, type QRCodeErrorCorrectionLevel, type QRCodeRenderersOptions, type QRCodeStringType } from 'qrcode';
|
||||
import { isRef, ref, watch } from 'vue';
|
||||
import type {
|
||||
CornerDotType,
|
||||
CornerSquareType,
|
||||
DotType,
|
||||
DrawType,
|
||||
ErrorCorrectionLevel,
|
||||
FileExtension,
|
||||
Mode,
|
||||
TypeNumber,
|
||||
} from 'pp-qr-code';
|
||||
import QRCodeStyling from 'pp-qr-code';
|
||||
|
||||
export function useQRCode({
|
||||
function blobToBase64(blob: Blob | null): Promise<string> {
|
||||
if (blob === null) {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
return new Promise((resolve, _reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result as string);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
export function useQRCodeStyling({
|
||||
text,
|
||||
color: { background, foreground },
|
||||
errorCorrectionLevel,
|
||||
options,
|
||||
imageBase64,
|
||||
imageOptions: { imageSize, margin: imageMargin },
|
||||
dotOptions: { type: dotType, color: dotColor },
|
||||
cornersSquareOptions: { type: cornersSquareType, color: cornersSquareColor },
|
||||
cornersDotOptions: { type: cornersDotType, color: cornersDotColor },
|
||||
outputType,
|
||||
width,
|
||||
margin,
|
||||
}: {
|
||||
text: MaybeRef<string>
|
||||
color: { foreground: MaybeRef<string>; background: MaybeRef<string> }
|
||||
errorCorrectionLevel?: MaybeRef<QRCodeErrorCorrectionLevel>
|
||||
options?: QRCodeToDataURLOptions
|
||||
outputType: MaybeRef<FileExtension>
|
||||
errorCorrectionLevel?: MaybeRef<ErrorCorrectionLevel>
|
||||
imageBase64?: MaybeRef<string>
|
||||
imageOptions: { imageSize: MaybeRef<number>; margin: MaybeRef<number> }
|
||||
dotOptions: { type: MaybeRef<DotType>; color: MaybeRef<string> }
|
||||
cornersSquareOptions: { type: MaybeRef<CornerSquareType>; color: MaybeRef<string> }
|
||||
cornersDotOptions: { type: MaybeRef<CornerDotType>; color: MaybeRef<string> }
|
||||
width?: MaybeRef<number>
|
||||
margin?: MaybeRef<number>
|
||||
}) {
|
||||
const qrcode = ref('');
|
||||
|
||||
watch(
|
||||
[text, background, foreground, errorCorrectionLevel].filter(isRef),
|
||||
[text, background, foreground, errorCorrectionLevel,
|
||||
imageBase64, imageSize, imageMargin,
|
||||
dotType, dotColor, cornersSquareType, cornersSquareColor,
|
||||
cornersDotType, cornersDotColor,
|
||||
outputType, width, margin].filter(isRef),
|
||||
async () => {
|
||||
if (get(text)) {
|
||||
qrcode.value = await QRCode.toDataURL(get(text).trim(), {
|
||||
color: {
|
||||
dark: get(foreground),
|
||||
light: get(background),
|
||||
...options?.color,
|
||||
const qrCodeText = get(text)?.trim();
|
||||
const qrCodeOutputType = get(outputType) || 'png';
|
||||
if (qrCodeText) {
|
||||
const getOrForeground = (colorRef: MaybeRef<string>) => {
|
||||
const color = get(colorRef) || get(foreground);
|
||||
if (color?.toLowerCase() === '#ffffffff') {
|
||||
return get(foreground);
|
||||
}
|
||||
return color;
|
||||
};
|
||||
const qrCodeOptions = {
|
||||
width: get(width) ?? 1024,
|
||||
height: get(width) ?? 1024,
|
||||
type: 'svg' as DrawType,
|
||||
data: qrCodeText,
|
||||
image: get(imageBase64),
|
||||
margin: get(margin) || 10,
|
||||
qrOptions: {
|
||||
typeNumber: 0 as TypeNumber,
|
||||
mode: 'Byte' as Mode,
|
||||
errorCorrectionLevel: get(errorCorrectionLevel) || 'Q',
|
||||
},
|
||||
errorCorrectionLevel: get(errorCorrectionLevel) ?? 'M',
|
||||
...options,
|
||||
});
|
||||
imageOptions: {
|
||||
hideBackgroundDots: true,
|
||||
imageSize: get(imageSize) || 0.4,
|
||||
margin: get(imageMargin) || 20,
|
||||
crossOrigin: 'anonymous',
|
||||
},
|
||||
dotsOptions: {
|
||||
color: getOrForeground(dotColor),
|
||||
// gradient: {
|
||||
// type: 'linear', // 'radial'
|
||||
// rotation: 0,
|
||||
// colorStops: [{ offset: 0, color: '#8688B2' }, { offset: 1, color: '#77779C' }]
|
||||
// },
|
||||
type: get(dotType) || 'square',
|
||||
},
|
||||
backgroundOptions: {
|
||||
color: get(background),
|
||||
// gradient: {
|
||||
// type: 'linear', // 'radial'
|
||||
// rotation: 0,
|
||||
// colorStops: [{ offset: 0, color: '#ededff' }, { offset: 1, color: '#e6e7ff' }]
|
||||
// },
|
||||
},
|
||||
cornersSquareOptions: {
|
||||
color: getOrForeground(cornersSquareColor),
|
||||
type: get(cornersSquareType) || 'extra-rounded',
|
||||
// gradient: {
|
||||
// type: 'linear', // 'radial'
|
||||
// rotation: 180,
|
||||
// colorStops: [{ offset: 0, color: '#25456e' }, { offset: 1, color: '#4267b2' }]
|
||||
// },
|
||||
},
|
||||
cornersDotOptions: {
|
||||
color: getOrForeground(cornersDotColor),
|
||||
type: get(cornersDotType) || 'dot',
|
||||
// gradient: {
|
||||
// type: 'linear', // 'radial'
|
||||
// rotation: 180,
|
||||
// colorStops: [{ offset: 0, color: '#00266e' }, { offset: 1, color: '#4060b3' }]
|
||||
// },
|
||||
},
|
||||
};
|
||||
|
||||
const qrGenerator = new QRCodeStyling(qrCodeOptions);
|
||||
qrcode.value = await blobToBase64(await qrGenerator.getRawData(qrCodeOutputType));
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
return { qrcode };
|
||||
}
|
||||
|
||||
export function useQRCode<QRCodeOptions extends QRCodeRenderersOptions>({
|
||||
text,
|
||||
color: { background, foreground },
|
||||
errorCorrectionLevel,
|
||||
outputType,
|
||||
width,
|
||||
options,
|
||||
}: {
|
||||
text: MaybeRef<string>
|
||||
color: { foreground: MaybeRef<string>; background: MaybeRef<string> }
|
||||
errorCorrectionLevel?: MaybeRef<QRCodeErrorCorrectionLevel>
|
||||
outputType?: MaybeRef<QRCodeDataURLType | QRCodeStringType>
|
||||
width?: MaybeRef<number>
|
||||
options?: QRCodeOptions
|
||||
}) {
|
||||
const qrcode = ref('');
|
||||
|
||||
watch(
|
||||
[text, background, foreground, errorCorrectionLevel, outputType, width].filter(isRef),
|
||||
async () => {
|
||||
const qrCodeText = get(text)?.trim();
|
||||
const qrCodeType = get(outputType) || '';
|
||||
if (qrCodeText) {
|
||||
const qrCodeOptions = {
|
||||
color: {
|
||||
dark: get(foreground),
|
||||
light: get(background),
|
||||
...options?.color,
|
||||
},
|
||||
errorCorrectionLevel: get(errorCorrectionLevel) ?? 'M',
|
||||
width: get(width) ?? 1024,
|
||||
...options,
|
||||
};
|
||||
if (['utf8', 'svg', 'terminal'].includes(qrCodeType)) {
|
||||
qrcode.value = await QRCode.toString(qrCodeText, {
|
||||
type: qrCodeType as QRCodeStringType,
|
||||
});
|
||||
}
|
||||
else {
|
||||
qrcode.value = await QRCode.toDataURL(qrCodeText, {
|
||||
...qrCodeOptions,
|
||||
type: qrCodeType as QRCodeDataURLType,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue