1
1
<script setup lang="ts">
2
- import type { QRCodeErrorCorrectionLevel } from ' qrcode' ;
3
- import { useQRCode } from ' ./useQRCode' ;
2
+ import type {
3
+ CornerDotType ,
4
+ CornerSquareType ,
5
+ DotType ,
6
+ ErrorCorrectionLevel ,
7
+ FileExtension ,
8
+ } from ' pp-qr-code' ;
9
+ import qrcodeConsole from ' qrcode-terminal-nooctal' ;
10
+ import { useQRCodeStyling } from ' ./useQRCode' ;
4
11
import { useDownloadFileFromBase64 } from ' @/composable/downloadBase64' ;
12
+ import { useQueryParamOrStorage } from ' @/composable/queryParams' ;
5
13
6
- const foreground = ref (' #000000ff' );
7
- const background = ref (' #ffffffff' );
8
- const errorCorrectionLevel = ref <QRCodeErrorCorrectionLevel >(' medium' );
14
+ const foreground = useQueryParamOrStorage ({ name: ' fg' , storageName: ' qr-code-gen:fg' , defaultValue: ' #000000ff' });
15
+ const background = useQueryParamOrStorage ({ name: ' bg' , storageName: ' qr-code-gen:bg' , defaultValue: ' #ffffffff' });
16
+ const errorCorrectionLevelSelectValue = useQueryParamOrStorage <string >({ name: ' level' , storageName: ' qr-code-gen:level' , defaultValue: ' medium' });
17
+ const errorCorrectionLevel = computed (() => errorCorrectionLevelSelectValue .value .toString ()[0 ].toUpperCase () as ErrorCorrectionLevel );
18
+ const width = useQueryParamOrStorage ({ name: ' width' , storageName: ' qr-code-gen:width' , defaultValue: 1024 });
19
+ const margin = useQueryParamOrStorage ({ name: ' margin' , storageName: ' qr-code-gen:margin' , defaultValue: 10 });
20
+ const imageSize = useQueryParamOrStorage ({ name: ' imgsize' , storageName: ' qr-code-gen:imsz' , defaultValue: 0.4 });
21
+ const imageMargin = useQueryParamOrStorage ({ name: ' imgmargin' , storageName: ' qr-code-gen:immg' , defaultValue: 20 });
22
+ const outputType = useQueryParamOrStorage <FileExtension >({ name: ' out' , storageName: ' qr-code-gen:out' , defaultValue: ' png' });
23
+ const dotType = useQueryParamOrStorage <DotType >({ name: ' dot' , storageName: ' qr-code-gen:dot' , defaultValue: ' square' });
24
+ const dotColor = useQueryParamOrStorage <string >({ name: ' dotc' , storageName: ' qr-code-gen:dotc' , defaultValue: ' #ffffffff' });
25
+ const cornersDotType = useQueryParamOrStorage <CornerDotType >({ name: ' cdt' , storageName: ' qr-code-gen:cdt' , defaultValue: ' square' });
26
+ const cornersDotColor = useQueryParamOrStorage <string >({ name: ' cdtc' , storageName: ' qr-code-gen:cdtc' , defaultValue: ' #ffffffff' });
27
+ const cornersSquareType = useQueryParamOrStorage <CornerSquareType >({ name: ' cst' , storageName: ' qr-code-gen:cst' , defaultValue: ' square' });
28
+ const cornersSquareColor = useQueryParamOrStorage <string >({ name: ' cstc' , storageName: ' qr-code-gen:cstc' , defaultValue: ' #ffffffff' });
29
+ const smallTerminal = useQueryParamOrStorage <boolean >({ name: ' sml' , storageName: ' qr-code-gen:sml' , defaultValue: false });
30
+ const fileInput = ref () as Ref <File >;
31
+ const { base64 : imageBase64 } = useBase64 (fileInput );
32
+ async function onUpload(file : File ) {
33
+ if (file ) {
34
+ fileInput .value = file ;
35
+ }
36
+ }
9
37
10
38
const errorCorrectionLevels = [' low' , ' medium' , ' quartile' , ' high' ];
39
+ const outputTypes = [' svg' , ' png' , ' jpeg' , ' webp' ];
40
+ const dotTypes = [' dots' ,
41
+ ' random-dots' ,
42
+ ' rounded' ,
43
+ ' vertical-lines' ,
44
+ ' horizontal-lines' ,
45
+ ' classy' ,
46
+ ' classy-rounded' ,
47
+ ' square' ,
48
+ ' extra-rounded' ];
49
+ const cornersDotTypes = [' dot' , ' square' , ' heart' ];
50
+ const cornersSquareTypes = [' dot' , ' square' , ' extra-rounded' ];
11
51
12
52
const text = ref (' https://it-tools.tech' );
13
- const { qrcode } = useQRCode ({
53
+ const { qrcode } = useQRCodeStyling ({
14
54
text ,
15
- color: {
16
- background ,
17
- foreground ,
18
- },
55
+ color: { background , foreground },
19
56
errorCorrectionLevel ,
20
- options: { width: 1024 },
57
+ imageBase64 ,
58
+ imageOptions: { imageSize , margin: imageMargin },
59
+ dotOptions: { type: dotType , color: dotColor },
60
+ cornersSquareOptions: { type: cornersSquareType , color: cornersSquareColor },
61
+ cornersDotOptions: { type: cornersDotType , color: cornersDotColor },
62
+ outputType ,
63
+ width ,
64
+ margin ,
21
65
});
22
66
23
- const { download } = useDownloadFileFromBase64 ({ source: qrcode , filename: ' qr-code.png' });
67
+ const qrcodeTerminal = computedAsync (() => {
68
+ const textValue = text .value ;
69
+ const level = errorCorrectionLevel .value ;
70
+ const small = smallTerminal .value ;
71
+ return new Promise <string >((resolve , _reject ) => {
72
+ try {
73
+ qrcodeConsole .setErrorLevel (level );
74
+ qrcodeConsole .generate (textValue , { small }, (qrcode : string ) => {
75
+ resolve (qrcode );
76
+ });
77
+ }
78
+ catch (_ ) {
79
+ resolve (' ' );
80
+ }
81
+ });
82
+ });
83
+
84
+ const filename = ref (' qr-code' );
85
+ const extension = computed (() => outputType .value .toString ());
86
+ const { download } = useDownloadFileFromBase64 ({ source: qrcode , filename , extension });
24
87
</script >
25
88
26
89
<template >
@@ -46,23 +109,119 @@ const { download } = useDownloadFileFromBase64({ source: qrcode, filename: 'qr-c
46
109
<n-form-item label =" Background color:" >
47
110
<n-color-picker v-model:value =" background" :modes =" ['hex']" />
48
111
</n-form-item >
112
+ <n-form-item label =" Width:" >
113
+ <n-input-number v-model:value =" width" :min =" 0" />
114
+ </n-form-item >
115
+ <n-form-item label =" Margin:" >
116
+ <n-input-number v-model:value =" margin" :min =" 0" />
117
+ </n-form-item >
49
118
<c-select
50
- v-model:value =" errorCorrectionLevel "
119
+ v-model:value =" errorCorrectionLevelSelectValue "
51
120
label =" Error resistance:"
52
121
label-position =" left"
53
122
label-width =" 130px"
54
123
label-align =" right"
55
124
:options =" errorCorrectionLevels.map((value) => ({ label: value, value }))"
56
125
/>
57
126
</n-form >
127
+ <c-card title =" Image" mt-3 >
128
+ <c-file-upload title =" Drag and drop an image here, or click to select an image" @file-upload =" onUpload" />
129
+
130
+ <n-form label-width =" 130" label-placement =" left" mt-3 >
131
+ <n-form-item label =" Size:" >
132
+ <n-input-number v-model:value =" imageSize" :min =" 0" step =" 0.1" />
133
+ </n-form-item >
134
+ <n-form-item label =" Margin:" >
135
+ <n-input-number v-model:value =" imageMargin" :min =" 0" />
136
+ </n-form-item >
137
+ </n-form >
138
+ </c-card >
139
+ <c-card mt-3 >
140
+ <details >
141
+ <summary >Dots Options</summary >
142
+ <n-form label-width =" 130" label-placement =" left" >
143
+ <n-form-item label =" Color:" >
144
+ <n-color-picker v-model:value =" dotColor" :modes =" ['hex']" />
145
+ </n-form-item >
146
+ <c-select
147
+ v-model:value =" dotType"
148
+ label =" Type:"
149
+ label-position =" left"
150
+ label-width =" 130px"
151
+ label-align =" right"
152
+ :options =" dotTypes.map((value) => ({ label: value, value }))"
153
+ />
154
+ </n-form >
155
+ </details >
156
+ </c-card >
157
+ <c-card mt-3 >
158
+ <details >
159
+ <summary >Corners Dots Options</summary >
160
+ <n-form label-width =" 130" label-placement =" left" >
161
+ <n-form-item label =" Color:" >
162
+ <n-color-picker v-model:value =" cornersDotColor" :modes =" ['hex']" />
163
+ </n-form-item >
164
+ <c-select
165
+ v-model:value =" cornersDotType"
166
+ label =" Type:"
167
+ label-position =" left"
168
+ label-width =" 130px"
169
+ label-align =" right"
170
+ :options =" cornersDotTypes.map((value) => ({ label: value, value }))"
171
+ />
172
+ </n-form >
173
+ </details >
174
+ </c-card >
175
+ <c-card mt-3 >
176
+ <details >
177
+ <summary >Corners Square Options</summary >
178
+ <n-form label-width =" 130" label-placement =" left" >
179
+ <n-form-item label =" Color:" >
180
+ <n-color-picker v-model:value =" cornersSquareColor" :modes =" ['hex']" />
181
+ </n-form-item >
182
+ <c-select
183
+ v-model:value =" cornersSquareType"
184
+ label =" Type:"
185
+ label-position =" left"
186
+ label-width =" 130px"
187
+ label-align =" right"
188
+ :options =" cornersSquareTypes.map((value) => ({ label: value, value }))"
189
+ />
190
+ </n-form >
191
+ </details >
192
+ </c-card >
193
+ <c-select
194
+ v-model:value =" outputType"
195
+ mt-3
196
+ label =" Output format:"
197
+ label-position =" left"
198
+ label-width =" 130px"
199
+ label-align =" right"
200
+ :options =" outputTypes.map((value) => ({ label: value.toUpperCase(), value }))"
201
+ />
58
202
</n-gi >
59
203
<n-gi >
60
204
<div flex flex-col items-center gap-3 >
61
205
<n-image :src =" qrcode" width =" 200" />
62
206
<c-button @click =" download" >
63
- Download qr-code
207
+ Download qr-code ({{ outputType.toString().toUpperCase() }})
64
208
</c-button >
65
209
</div >
210
+
211
+ <n-divider />
212
+
213
+ <n-checkbox v-model:checked =" smallTerminal" >
214
+ Small Terminal
215
+ </n-checkbox >
216
+ <n-form-item label =" Terminal output:" mt-1 >
217
+ <TextareaCopyable
218
+ :value =" qrcodeTerminal"
219
+ multiline
220
+ rows =" 5"
221
+ mb-1 mt-1
222
+ copy-placement =" outside"
223
+ />
224
+ </n-form-item >
66
225
</n-gi >
67
226
</n-grid >
68
227
</c-card >
0 commit comments