feat(tool): added qr-code generator
This commit is contained in:
		
							parent
							
								
									27f3826d5f
								
							
						
					
					
						commit
						c16e537abf
					
				
							
								
								
									
										57
									
								
								components/ColorInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								components/ColorInput.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| <template> | ||||
|   <v-text-field | ||||
|     v-model="color" | ||||
|     hide-details | ||||
|     class="ma-0 pa-0" | ||||
|     outlined | ||||
|     :label="label" | ||||
|     @input="$emit('input', color)" | ||||
|   > | ||||
|     <template v-slot:append> | ||||
|       <v-menu v-model="menu" top nudge-bottom="101" nudge-left="16" :close-on-content-click="false"> | ||||
|         <template v-slot:activator="{ on }"> | ||||
|           <div :style="swatchStyle" v-on="on"/> | ||||
|         </template> | ||||
|         <v-card> | ||||
|           <v-card-text class="pa-0"> | ||||
|             <v-color-picker v-model="color" flat @input="$emit('input', color)"/> | ||||
|           </v-card-text> | ||||
|         </v-card> | ||||
|       </v-menu> | ||||
|     </template> | ||||
|   </v-text-field> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import {Component, Prop, Vue} from 'nuxt-property-decorator' | ||||
| // Adapted from: https://codepen.io/JamieCurnow/pen/KKPjraK | ||||
| 
 | ||||
| @Component | ||||
| export default class ColorInput extends Vue { | ||||
|   @Prop({default: '#ffffff'}) readonly value!: string; | ||||
|   @Prop() readonly label: string | undefined; | ||||
|   menu = false | ||||
|   color = '' | ||||
| 
 | ||||
|   created() { | ||||
|     this.color = this.value | ||||
|   } | ||||
| 
 | ||||
|   get swatchStyle() { | ||||
|     return { | ||||
|       backgroundColor: this.color, | ||||
|       cursor: 'pointer', | ||||
|       height: '30px', | ||||
|       width: '30px', | ||||
|       borderRadius: this.menu ? '50%' : '4px', | ||||
|       transition: 'border-radius 200ms ease-in-out' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="less"> | ||||
| ::v-deep .v-input__append-inner { | ||||
|   margin-top: 13px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -14222,6 +14222,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", | ||||
|       "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" | ||||
|     }, | ||||
|     "qrcode.vue": { | ||||
|       "version": "1.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-1.7.0.tgz", | ||||
|       "integrity": "sha512-R7t6Y3fDDtcU7L4rtqwGUDP9xD64gJhIwpfjhRCTKmBoYF6SS49PIJHRJ048cse6OI7iwTwgyy2C46N9Ygoc6g==" | ||||
|     }, | ||||
|     "qs": { | ||||
|       "version": "6.10.1", | ||||
|       "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", | ||||
|  | ||||
| @ -25,6 +25,7 @@ | ||||
|     "crypto-js": "^4.0.0", | ||||
|     "nuxt": "^2.15.6", | ||||
|     "nuxt-i18n": "^6.27.0", | ||||
|     "qrcode.vue": "^1.7.0", | ||||
|     "vuetify": "^2.5.0", | ||||
|     "vuetify-toast-snackbar": "^0.6.1" | ||||
|   }, | ||||
|  | ||||
							
								
								
									
										134
									
								
								pages/tools/web/qrcode-generator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								pages/tools/web/qrcode-generator.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| <template> | ||||
|   <ToolWrapper :config="config()"> | ||||
|     <v-text-field | ||||
|       v-model="value" | ||||
|       outlined | ||||
|       label="Data" | ||||
|       :rules="rules.value" | ||||
|     /> | ||||
|     <v-slider v-model="size" min="100" max="1920" label="Size (preview will not change): " thumb-label /> | ||||
|     <v-select | ||||
|       v-model="level" | ||||
|       outlined | ||||
|       :items="levels" | ||||
|       label="Error resistance" | ||||
|     /> | ||||
|     <v-row> | ||||
|       <v-col cols="12" md="6" sm="12"> | ||||
|         <ColorInput v-model="fgColor" label="Foreground color" /> | ||||
|       </v-col> | ||||
|       <v-col cols="12" md="6" sm="12"> | ||||
|         <ColorInput v-model="bgColor" label="Background color" /> | ||||
|       </v-col> | ||||
|     </v-row> | ||||
| 
 | ||||
|     <div class="text-center mt-5 mb-5"> | ||||
|       <qrcode-vue | ||||
|         :value="value" | ||||
|         :size="size" | ||||
|         :level="level" | ||||
|         :background="bgColor" | ||||
|         :foreground="fgColor" | ||||
|         render-as="svg" | ||||
|         class-name="qrcode-wrapper" | ||||
|       /> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="text-center mb-sm-2"> | ||||
|       <v-btn class="mr-1" color="primary" @click="download('png')"> | ||||
|         download as png | ||||
|       </v-btn> | ||||
|       <v-btn class="ml-1" color="primary" @click="download('svg')"> | ||||
|         download as svg | ||||
|       </v-btn> | ||||
|     </div> | ||||
|   </ToolWrapper> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import {Component} from 'nuxt-property-decorator' | ||||
| import QrcodeVue from 'qrcode.vue' | ||||
| import colors from 'color-name' | ||||
| import {CopyableMixin} from '~/mixins/copyable.mixin' | ||||
| import Tool from '~/components/Tool.vue' | ||||
| import type {ToolConfig} from '~/types/ToolConfig' | ||||
| import {downloadBase64File} from '~/utils/file' | ||||
| import {stringToBase64} from '~/utils/convert' | ||||
| import ColorInput from '~/components/ColorInput.vue' | ||||
| 
 | ||||
| @Component({ | ||||
|   components: {QrcodeVue, ColorInput}, | ||||
|   mixins: [CopyableMixin] | ||||
| }) | ||||
| export default class QrcodeGenerator extends Tool { | ||||
|   config(): ToolConfig { | ||||
|     return { | ||||
|       title: 'QR-code generator', | ||||
|       description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.', | ||||
|       icon: 'mdi-qrcode', | ||||
|       keywords: ['editor'] | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   value = 'https://it-tools.tech' | ||||
|   size = 300 | ||||
|   level = 'M' | ||||
|   bgColor = 'transparent' | ||||
|   fgColor = '#ffffff' | ||||
|   levels = [ | ||||
|     {text: 'Low', value: 'L'}, | ||||
|     {text: 'Medium', value: 'M'}, | ||||
|     {text: 'Quartile', value: 'Q'}, | ||||
|     {text: 'High', value: 'H'} | ||||
|   ] | ||||
| 
 | ||||
|   rules = { | ||||
|     value: [ | ||||
|       (v: string) => v.length > 0 || 'Value is needed' | ||||
|     ], | ||||
|     color: [ | ||||
|       (v: string) => { | ||||
|         v = v.trim() | ||||
|         const isFFFFFF = /^#[0-9a-fA-F]{6}$/.test(v) | ||||
|         const isFFF = /^#[0-9a-fA-F]{3}$/.test(v) | ||||
|         const isRGB = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.test(v) | ||||
|         const isHSL = /^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/.test(v) | ||||
|         const isKeyword = v in colors | ||||
|         const isTransparent = v === 'transparent' | ||||
|         return isFFFFFF || isFFF || isKeyword || isTransparent || isRGB || isHSL || 'Incorrect color.' | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| 
 | ||||
|   download(type: string) { | ||||
|     const svgEl = this.$el.querySelector('.qrcode-wrapper svg')! | ||||
|     const svgString = new XMLSerializer().serializeToString(svgEl) | ||||
|     const svgUrl = `data:image/svg+xml;base64,${stringToBase64(svgString)}` | ||||
|     if (type === 'png') { | ||||
|       const canvas = document.createElement('canvas') | ||||
|       canvas.width = this.size | ||||
|       canvas.height = this.size | ||||
|       const ctx = canvas.getContext('2d')! | ||||
|       const image = new Image() | ||||
|       image.onload = function () { | ||||
|         ctx.drawImage(image, 0, 0) | ||||
|         const result = canvas.toDataURL() | ||||
|         downloadBase64File(result, 'qr-code') | ||||
|       } | ||||
|       image.src = svgUrl | ||||
|     } else { | ||||
|       downloadBase64File(svgUrl, 'qr-code') | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="less"> | ||||
| ::v-deep .qrcode-wrapper { | ||||
|   & > * { | ||||
|     width: 300px !important; | ||||
|     height: 300px !important; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										1
									
								
								types/qrcode.vue.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								types/qrcode.vue.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| declare module 'qrcode.vue'; | ||||
							
								
								
									
										10
									
								
								utils/file.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								utils/file.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| const downloadBase64File = (dataUrl: string, name = 'file') => { | ||||
|   const a = document.createElement('a') | ||||
|   a.href = dataUrl | ||||
|   a.download = name | ||||
|   a.click() | ||||
| } | ||||
| 
 | ||||
| export { | ||||
|   downloadBase64File | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user