added Contact Info QR Code generator
This commit is contained in:
		
							parent
							
								
									a6fc0b5448
								
							
						
					
					
						commit
						2a5fa4bab1
					
				| @ -257,6 +257,10 @@ tools: | ||||
|     title: WiFi QR Code generator | ||||
|     description: Generate and download QR codes for quick connections to WiFi networks. | ||||
| 
 | ||||
|   qr-contact-info-generator: | ||||
|     title: QR Contact Info generator | ||||
|     description: Generate and download QR codes for Contact information (vCard), and customize the background and foreground colors. | ||||
| 
 | ||||
|   xml-formatter: | ||||
|     title: XML formatter | ||||
|     description: Prettify your XML string into a friendly, human-readable format. | ||||
|  | ||||
| @ -142,7 +142,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|   }, | ||||
|   { | ||||
|     name: 'Images and videos', | ||||
|     components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder], | ||||
|     components: [qrCodeGenerator, wifiQrCodeGenerator, qrContactInfoGenerator, svgPlaceholderGenerator, cameraRecorder], | ||||
|   }, | ||||
|   { | ||||
|     name: 'Development', | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| import { Qrcode } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate } from '@/plugins/i18n.plugin'; | ||||
| import { Qrcode } from "@vicons/tabler"; | ||||
| import { defineTool } from "../tool"; | ||||
| import { translate } from "@/plugins/i18n.plugin"; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: translate('tools.qr-contact-info-generator.title'), | ||||
|   path: '/qr-contact-info-generator', | ||||
|   description: translate('tools.qr-contact-info-generator.description'), | ||||
|   keywords: ['qr', 'contact', 'info', 'generator'], | ||||
|   component: () => import('./qr-contact-info-generator.vue'), | ||||
|   name: translate("tools.qr-contact-info-generator.title"), | ||||
|   path: "/qr-contact-info-generator", | ||||
|   description: translate("tools.qr-contact-info-generator.description"), | ||||
|   keywords: ["qr", "contact", "vcard", "generator", "business", "networking"], | ||||
|   component: () => import("./qr-contact-info-generator.vue"), | ||||
|   icon: Qrcode, | ||||
|   createdAt: new Date('2025-02-17'), | ||||
| }); | ||||
|   createdAt: new Date(), | ||||
| }); | ||||
|  | ||||
| @ -1,56 +0,0 @@ | ||||
| import { type MaybeRef, get } from '@vueuse/core'; | ||||
| import QRCode, { type QRCodeErrorCorrectionLevel, type QRCodeToDataURLOptions } from 'qrcode'; | ||||
| import { isRef, ref, watch } from 'vue'; | ||||
| 
 | ||||
| interface ContactInfo { | ||||
|   firstName: string; | ||||
|   lastName: string; | ||||
|   jobRole: string; | ||||
|   phoneNumber: string; | ||||
|   emailAddress: string; | ||||
|   companyName: string; | ||||
|   companyWebsite: string; | ||||
|   companyAddress: string; | ||||
| } | ||||
| 
 | ||||
| interface QRCodeOptions { | ||||
|   foregroundColor?: string; | ||||
|   backgroundColor?: string; | ||||
| } | ||||
| 
 | ||||
| export function generateContactQRCode(contact: MaybeRef<ContactInfo>, options: MaybeRef<QRCodeOptions> = {}): Promise<string> { | ||||
|   const vCard = ref(` | ||||
| BEGIN:VCARD | ||||
| VERSION:3.0 | ||||
| FN:${get(contact).firstName} ${get(contact).lastName} | ||||
| ORG:${get(contact).companyName} | ||||
| TITLE:${get(contact).jobRole} | ||||
| TEL:${get(contact).phoneNumber} | ||||
| EMAIL:${get(contact).emailAddress} | ||||
| URL:${get(contact).companyWebsite} | ||||
| ADR:${get(contact).companyAddress} | ||||
| END:VCARD | ||||
|   `);
 | ||||
| 
 | ||||
|   const qrOptions: QRCodeToDataURLOptions = { | ||||
|     errorCorrectionLevel: 'H' as QRCodeErrorCorrectionLevel, | ||||
|     type: 'image/png', | ||||
|     margin: 1, | ||||
|     color: { | ||||
|       dark: get(options).foregroundColor || '#000000ff', | ||||
|       light: get(options).backgroundColor || '#ffffffff', | ||||
|     }, | ||||
|   }; | ||||
| 
 | ||||
|   const qrCodeDataUrl = ref(''); | ||||
| 
 | ||||
|   watch( | ||||
|     [vCard, options].filter(isRef), | ||||
|     async () => { | ||||
|       qrCodeDataUrl.value = await QRCode.toDataURL(get(vCard), qrOptions); | ||||
|     }, | ||||
|     { immediate: true }, | ||||
|   ); | ||||
| 
 | ||||
|   return qrCodeDataUrl.value; | ||||
| } | ||||
| @ -1,77 +1,61 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <h1>Contact QR Code Generator</h1> | ||||
|     <form @submit.prevent="generateQRCode"> | ||||
|       <input v-model="contact.firstName" placeholder="First Name" required /> | ||||
|       <input v-model="contact.lastName" placeholder="Last Name" required /> | ||||
|       <input v-model="contact.jobRole" placeholder="Job Role" required /> | ||||
|       <input v-model="contact.phoneNumber" placeholder="Phone Number" required /> | ||||
|       <input v-model="contact.emailAddress" placeholder="Email Address" required /> | ||||
|       <input v-model="contact.companyName" placeholder="Company Name" required /> | ||||
|       <input v-model="contact.companyWebsite" placeholder="Company Website" required /> | ||||
|       <input v-model="contact.companyAddress" placeholder="Company Address" required /> | ||||
|       <input v-model="foregroundColor" placeholder="Foreground Color" /> | ||||
|       <input v-model="backgroundColor" placeholder="Background Color" /> | ||||
|       <button type="submit">Generate QR Code</button> | ||||
|     </form> | ||||
|     <div v-if="qrCodeDataUrl"> | ||||
|       <img :src="qrCodeDataUrl" alt="Contact QR Code" /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { useContactQRCode } from "./useContactQRCode"; | ||||
| import { useDownloadFileFromBase64 } from "@/composable/downloadBase64"; | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineComponent, ref } from 'vue'; | ||||
| import { generateContactQRCode } from './qr-contact-info-generator'; | ||||
| const fullName = ref(""); | ||||
| const jobRole = ref(""); | ||||
| const phoneNumber = ref(""); | ||||
| const email = ref(""); | ||||
| const website = ref(""); | ||||
| const address = ref(""); | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|   name: 'ContactQRCodeGenerator', | ||||
|   setup() { | ||||
|     const contact = ref({ | ||||
|       firstName: '', | ||||
|       lastName: '', | ||||
|       jobRole: '', | ||||
|       phoneNumber: '', | ||||
|       emailAddress: '', | ||||
|       companyName: '', | ||||
|       companyWebsite: '', | ||||
|       companyAddress: '' | ||||
|     }); | ||||
|     const foregroundColor = ref('#000000ff'); | ||||
|     const backgroundColor = ref('#ffffffff'); | ||||
|     const qrCodeDataUrl = ref<string | null>(null); | ||||
| const foreground = ref("#000000ff"); | ||||
| const background = ref("#ffffffff"); | ||||
| 
 | ||||
|     const generateQRCode = async () => { | ||||
|       qrCodeDataUrl.value = await generateContactQRCode(contact.value, { | ||||
|         foregroundColor: foregroundColor.value, | ||||
|         backgroundColor: backgroundColor.value | ||||
|       }); | ||||
|     }; | ||||
| const { qrcode } = useContactQRCode({ | ||||
|   fullName, | ||||
|   jobRole, | ||||
|   phoneNumber, | ||||
|   email, | ||||
|   website, | ||||
|   address, | ||||
|   color: { foreground, background }, | ||||
|   options: { width: 1024 }, | ||||
| }); | ||||
| 
 | ||||
|     return { | ||||
|       contact, | ||||
|       foregroundColor, | ||||
|       backgroundColor, | ||||
|       qrCodeDataUrl, | ||||
|       generateQRCode | ||||
|     }; | ||||
|   } | ||||
| const { download } = useDownloadFileFromBase64({ | ||||
|   source: qrcode, | ||||
|   filename: "contact-info-qr.png", | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| form { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   gap: 10px; | ||||
| } | ||||
| <template> | ||||
|   <c-card> | ||||
|     <div grid grid-cols-1 gap-12> | ||||
|       <div> | ||||
|         <c-input-text v-model:value="fullName" label="Full Name" placeholder="John Doe" mb-4 /> | ||||
|         <c-input-text v-model:value="jobRole" label="Job Role" placeholder="Software Engineer" mb-4 /> | ||||
|         <c-input-text v-model:value="phoneNumber" label="Phone Number" placeholder="+1 234 567 8901" mb-4 /> | ||||
|         <c-input-text v-model:value="email" label="Email Address" placeholder="john.doe@example.com" mb-4 /> | ||||
|         <c-input-text v-model:value="website" label="Website" placeholder="https://acme.com" mb-4 /> | ||||
|         <c-input-text v-model:value="address" label="Company Address" placeholder="123 Main St, City" mb-4 /> | ||||
| 
 | ||||
| button { | ||||
|   margin-top: 10px; | ||||
| } | ||||
|         <n-form label-width="130" label-placement="left"> | ||||
|           <n-form-item label="Foreground color:"> | ||||
|             <n-color-picker v-model:value="foreground" :modes="['hex']" /> | ||||
|           </n-form-item> | ||||
|           <n-form-item label="Background color:"> | ||||
|             <n-color-picker v-model:value="background" :modes="['hex']" /> | ||||
|           </n-form-item> | ||||
|         </n-form> | ||||
|       </div> | ||||
| 
 | ||||
| img { | ||||
|   margin-top: 20px; | ||||
|   max-width: 100%; | ||||
| } | ||||
| </style> | ||||
|       <div v-if="qrcode"> | ||||
|         <div flex flex-col items-center gap-3> | ||||
|           <img alt="contact-info-qrcode" :src="qrcode" width="200" /> | ||||
|           <c-button @click="download">Download QR Code</c-button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </c-card> | ||||
| </template> | ||||
|  | ||||
							
								
								
									
										49
									
								
								src/tools/qr-contact-info-generator/useContactQRCode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/tools/qr-contact-info-generator/useContactQRCode.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| import { computed, ref, watchEffect } from "vue"; | ||||
| import QRCode, { type QRCodeToDataURLOptions } from "qrcode"; | ||||
| import { get, MaybeRef } from "@vueuse/core"; | ||||
| 
 | ||||
| interface IContactQRCodeOptions { | ||||
|   fullName: MaybeRef<string>; | ||||
|   jobRole: MaybeRef<string>; | ||||
|   phoneNumber: MaybeRef<string>; | ||||
|   email: MaybeRef<string>; | ||||
|   website: MaybeRef<string>; | ||||
|   address: MaybeRef<string>; | ||||
|   color: { foreground: MaybeRef<string>; background: MaybeRef<string> }; | ||||
|   options?: QRCodeToDataURLOptions; | ||||
| } | ||||
| 
 | ||||
| // Computed vCard to ensure reactivity works correctly
 | ||||
| const useVCard = (options: IContactQRCodeOptions) => { | ||||
|   return computed(() => { | ||||
|     return `BEGIN:VCARD
 | ||||
| VERSION:3.0 | ||||
| ${get(options.fullName) ? `FN:${get(options.fullName)}` : ""} | ||||
| ${get(options.jobRole) ? `TITLE:${get(options.jobRole)}` : ""} | ||||
| ${get(options.phoneNumber) ? `TEL:${get(options.phoneNumber)}` : ""} | ||||
| ${get(options.email) ? `EMAIL:${get(options.email)}` : ""} | ||||
| ${get(options.website) ? `URL:${get(options.website)}` : ""} | ||||
| ${get(options.address) ? `ADR:${get(options.address)}` : ""} | ||||
| END:VCARD`.trim();
 | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export function useContactQRCode(options: IContactQRCodeOptions) { | ||||
|   const qrcode = ref(""); | ||||
|   const vCardText = useVCard(options); // Computed property for reactivity
 | ||||
| 
 | ||||
|   watchEffect(async () => { | ||||
|     if (vCardText.value) { | ||||
|       qrcode.value = await QRCode.toDataURL(vCardText.value, { | ||||
|         color: { | ||||
|           dark: get(options.color.foreground), | ||||
|           light: get(options.color.background), | ||||
|         }, | ||||
|         errorCorrectionLevel: "M", | ||||
|         ...options.options, | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   return { qrcode }; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user