在现代Web应用中,二维码(QR Code)生成功能越来越受到开发者和用户的青睐。无论是用于分享链接、支付信息还是其他数据,二维码都能提供便捷的解决方案。然而,传统的二维码生成实现方式往往缺乏灵活性和交互性。本文将介绍如何结合DeepSeek和Vue框架,打造一款丝滑、智能且高度可定制的二维码生成组件,为您的Web应用增添更多便捷和互动性。
📚前言
日活用户数方面,DeepSeek 的增长势头同样强劲。国内 AI 产品榜的最新统计数据显示,DeepSeek 应用在上线仅 20 天后,其日活跃用户数(DAU)迅速突破 2000 万大关,具体数字高达 2215 万 。这一成绩不仅彰显了 DeepSeek 强大的用户吸引力,也进一步证明了 AI 技术在当今社会的广泛应用和巨大潜力。到 2 月 1 日,DeepSeek 日活跃用户突破 3000 万大关,成为史上最快达到这一里程碑的应用 。其日活跃用户数已达到 ChatGPT 日活跃用户数的 41.6%,并成功超越了豆包的日活跃用户数 1695 万 。在上线仅 21 天后,DeepSeek 的月活跃用户数(MAU)便达到了 3370 万,使其在全球 AI 产品月活总榜上成功跻身前四 。
DeepSeek 的出现,对美国科技股和全球 AI 市场产生了深远的影响。在美股市场,1 月 27 日,美股 AI、芯片股重挫,英伟达收盘大跌超过 17%,单日市值蒸发 5890 亿美元,创下美国股市历史上最高纪录 。这一现象反映出 DeepSeek 的崛起对美国科技巨头的市场地位构成了挑战,引发了投资者对科技股未来前景的担忧。
📚页面效果
📚指令输入
已经创建好了一个基于Vue3的组合式API的项目(Composition API),并能正常运行起来,请帮我用 Vue3的组合式API(Composition API) 生成一个 二维码生成(QR Code) 的功能组件,所有代码都保存在components/QRCode 下的文件夹中。功能组件的script标签中只有setup属性,使用普通 JavaScript 实现,不使用TypeScript。
功能要有,如下属性:
属性定义
基本内容属性
value:这是最核心的属性,用于指定要编码到二维码中的数据,比如文本、链接等。二维码的生成就是基于这个值来进行的,它可以是字符串类型,用户需要根据实际需求传入相应的内容。
type:指定二维码的纠错级别。纠错级别决定了二维码在部分损坏的情况下仍能被正确扫描的能力,常见的取值有L(低,7%纠错)、M(中,15%纠错)、Q(高,25%纠错)、H(最高,30%纠错)。
样式属性
size:用于控制二维码的尺寸大小,单位可以是像素(px)。它决定了二维码在页面上显示的物理大小,方便根据不同的布局需求进行调整。
colorDark:设置二维码中深色模块(通常是黑色)的颜色。可以使用十六进制颜色码、RGB 或 RGBA 等格式来指定颜色,满足不同的设计风格。
colorLight:设置二维码中浅色模块(通常是白色)的颜色,同样可以使用十六进制颜色码、RGB 或 RGBA 等格式。
margin:定义二维码周围的空白边距,单位为像素。适当的边距可以使二维码在页面上更加美观,也有助于提高扫描的成功率。
其他属性
version:手动指定二维码的版本号。二维码版本从 1 到 40,版本越高,所能容纳的数据量就越大。一般情况下不需要手动指定,组件会根据传入的数据自动选择合适的版本,但在某些特殊需求下可以使用该属性。
事件定义
@generated:当二维码成功生成时触发该事件,事件回调函数中可以获取到生成的二维码对象或者相关的状态信息,方便开发者在二维码生成后进行后续的操作,比如将二维码保存为图片等。
@error:在二维码生成过程中出现错误时触发该事件,事件回调函数会携带错误信息,开发者可以根据错误信息进行相应的处理,如提示用户重新输入数据等。
其他
性能优化
采用虚拟列表技术:如果在一个页面中需要生成多个二维码,使用虚拟列表可以只渲染当前可见区域的二维码,减少不必要的渲染开销,提高页面性能。
缓存机制:对于相同数据生成的二维码进行缓存,避免重复生成,提高生成效率。
兼容性
确保组件在不同浏览器和设备上都能正常显示和使用,进行充分的兼容性测试,特别是在移动设备上,要考虑不同屏幕分辨率和浏览器内核的差异。
可访问性
为二维码添加适当的alt属性,当二维码无法正常显示时,屏幕阅读器可以读取该属性内容,为视觉障碍用户提供必要的信息。
扩展性
提供扩展接口,允许开发者自定义二维码的生成算法或者样式处理逻辑,方便根据不同的业务需求进行定制化开发。
你有更好的建议也可以添加,要注明。组件定义好后给出5个及以上的调用示例。
下面是现有目录
vueAndDeepseek/
├── src/ # 源代码目录
│ ├── assets/ # 静态资源
│ │ ├── base.css
│ │ ├── main.css
│ │ └── logo.svg
│ ├── components/ # 组件目录
│ │ ├── HelloWorld.vue
│ │ ├── TheWelcome.vue
│ │ ├── WelcomeItem.vue
│ │ ├── Progress/
│ │ │ └── Progress.vue
│ │ ├── Accordion/
│ │ ├── BackToTop/
│ │ ├── Card/
│ │ ├── InfiniteScroll/
│ │ ├── Notification/
│ │ ├── Timeline/
│ │ ├── Switch/
│ │ ├── Tabs/
│ │ ├── Sidebar/
│ │ ├── Breadcrumbs/
│ │ ├── MasonryLayout/
│ │ ├── Rating/
│ │ ├── ColorPicker/
│ │ ├── RightClickMenu/
│ │ ├── RangePicker/
│ │ ├── Navbar/
│ │ ├── FormValidation/
│ │ ├── CopyToClipboard/
│ │ ├── ClickAnimations/
│ │ ├── ThumbnailList/
│ │ ├── KeyboardShortcuts/
│ │ ├── CommentSystem/
│ │ ├── QRCode/
│ │ ├── DatePicker/
│ │ └── icons/
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # Pinia 状态管理
│ │ └── counter.js
│ ├── views/ # 页面组件
│ │ ├── ProgressView.vue
│ │ ├── TabsView.vue
│ │ ├── SwitchView.vue
│ │ ├── TimelineView.vue
│ │ ├── NotificationView.vue
│ │ ├── CardView.vue
│ │ ├── InfiniteScrollView.vue
│ │ ├── BackToTopView.vue
│ │ ├── AccordionView.vue
│ │ ├── SidebarView.vue
│ │ ├── BreadcrumbsView.vue
│ │ ├── MasonryLayoutView.vue
│ │ ├── RatingView.vue
│ │ ├── ColorPickerView.vue
│ │ ├── RightClickMenuView.vue
│ │ ├── RangePickerView.vue
│ │ ├── NavbarView.vue
│ │ ├── FormValidationView.vue
│ │ ├── CopyToClipboardView.vue
│ │ ├── ClickAnimationsView.vue
│ │ ├── ThumbnailListView.vue
│ │ ├── KeyboardShortcutsView.vue
│ │ ├── CommentSystemView.vue
│ │ ├── QRCodeView.vue
│ │ ├── DatePickerView.vue
│ │ └── AboutView.vue
│ ├── App.vue # 根组件
│ └── main.js # 应用入口
├── public/ # 公共资源目录
├── index.html # HTML 模板
├── package.json # 项目配置
├── vite.config.js # Vite 配置
└── node_modules/ # 依赖包目录
📘组件代码
下面按照您的要求创建二维码组件:
components/QRCode/QRCode.vue
<template> <div class="qr-code"> <div ref="qrCodeRef"></div> <span class="sr-only">QR Code: {{ value }}</span> </div> </template> <script setup> import { ref, watch, onMounted, onBeforeUnmount } from 'vue' import QRCode from 'qrcodejs2' const props = defineProps({ value: { type: String, required: true }, type: { type: String, default: 'H', validator(value) { return ['L', 'M', 'Q', 'H'].includes(value.toUpperCase()) } }, size: { type: Number, default: 200 }, colorDark: { type: String, default: '#000000' }, colorLight: { type: String, default: '#ffffff' }, margin: { type: Number, default: 4 }, version: { type: Number, default: undefined } }) const emit = defineEmits(['generated', 'error']) const qrCodeRef = ref(null) let qrCodeInstance = null const cache = new Map() const generateCacheKey = () => { return JSON.stringify({ value: props.value, type: props.type, size: props.size, colorDark: props.colorDark, colorLight: props.colorLight, margin: props.margin, version: props.version }) } const generateQRCode = () => { if (!props.value) { emit('error', new Error('QR code value is required')) return } const cacheKey = generateCacheKey() if (cache.has(cacheKey)) { updateFromCache(cache.get(cacheKey)) return } try { if (qrCodeInstance) { qrCodeInstance.clear() qrCodeInstance = null } qrCodeInstance = new QRCode(qrCodeRef.value, { text: props.value, width: props.size, height: props.size, colorDark: props.colorDark, colorLight: props.colorLight, correctLevel: QRCode.CorrectLevel[props.type.toUpperCase()], cellspacing: props.margin, version: props.version }) observeQRGeneration(cacheKey) } catch (error) { emit('error', error) } } const observeQRGeneration = (cacheKey) => { const observer = new MutationObserver(() => { const img = qrCodeRef.value.querySelector('img') const canvas = qrCodeRef.value.querySelector('canvas') if (img) { img.onload = () => handleSuccess(img.src, cacheKey) img.alt = `QR Code: ${props.value.substring(0, 50)}` } else if (canvas) { handleSuccess(canvas.toDataURL(), cacheKey) } observer.disconnect() }) observer.observe(qrCodeRef.value, { childList: true }) } const handleSuccess = (dataUrl, cacheKey) => { cache.set(cacheKey, dataUrl) emit('generated', dataUrl) } const updateFromCache = (dataUrl) => { qrCodeRef.value.innerHTML = ` <img src="https://blog.csdn.net/qq_33650655/article/details/${{C}dataUrl}" alt="QR Code: ${{C}props.value.substring(0, 50)}" style="width: ${{C}props.size}px; height: ${{C}props.size}px" > ` emit('generated', dataUrl) } onMounted(generateQRCode) onBeforeUnmount(() => qrCodeInstance?.clear()) watch(() => generateCacheKey(), generateQRCode) </script> <style scoped> .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .qr-code { display: inline-block; line-height: 0; } </style>
调用示例 (views/QRCodeView.vue):
<template> <div class="examples"> <!-- 基础用法 --> <QRCode value="https://deepseek.com" /> <!-- 自定义颜色和尺寸 --> <QRCode value="https://deepseek.com" :size="250" color-dark="#2c3e50" color-light="#ecf0f1" /> <!-- 带边距和事件处理 --> <QRCode value="Event Example" :margin="10" @generated="handleGenerated" @error="handleError" /> <!-- 指定版本和纠错级别 --> <QRCode value="Version Control Example" :version="5" type="Q" /> <!-- 动态数据绑定 --> <input v-model="dynamicValue" placeholder="输入二维码内容"> <QRCode :value="dynamicValue" /> </div> </template> <script setup> import { ref } from 'vue' import QRCode from '@/components/QRCode/QRCode.vue' const dynamicValue = ref('') const handleGenerated = (dataUrl) => { console.log('生成成功:', dataUrl) } const handleError = (error) => { console.error('生成失败:', error) } </script> <style scoped> .examples { display: grid; gap: 2rem; padding: 2rem; } input { padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; width: 300px; } </style>
组件特点:
使用qrcodejs2实现二维码生成
添加了缓存机制提升性能
支持响应式属性变化
完善的错误处理和事件通知
可访问性优化(屏幕阅读器支持)
自动清理旧实例避免内存泄漏
MutationObserver监听DOM变化确保事件触发
类型校验和参数验证
扩展建议:
可以添加二维码下载功能
支持二维码Logo叠加
添加loading状态提示
支持扫描结果解码预览
添加二维码有效性验证功能
📚代码测试
会有一个报错
TypeError: Cannot read properties of undefined (reading '_android')
📚整理后主要代码
📘定义组件 \src\components\QRCode\QRCode.vue
<template> <div class="qr-code"> <QrcodeVue :value="value" :size="size" :level="type" :background="colorLight" :foreground="colorDark" :margin="margin" :version="version" @render="handleGenerated" @renderError="handleError" /> <span class="sr-only">QR Code: {{ value }}</span> </div> </template> <script setup> import {onMounted} from 'vue' import QrcodeVue from 'qrcode.vue' const props = defineProps({ value: { type: String, required: true }, type: { type: String, default: 'H', validator(value) { return ['L', 'M', 'Q', 'H'].includes(value.toUpperCase()) } }, size: { type: Number, default: 200 }, colorDark: { type: String, default: '#000000' }, colorLight: { type: String, default: '#ffffff' }, margin: { type: Number, default: 4 }, version: { type: Number, default: undefined } }) const emit = defineEmits(['generated', 'error']) const handleGenerated = (dataUrl) => { emit('generated', dataUrl) } const handleError = (error) => { emit('error', error) } onMounted(() => { if (!props.value) { emit('error', new Error('QR code value is required')) } }) </script> <style scoped> .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .qr-code { display: inline-block; line-height: 0; } </style>
📘调用 \src\views\QRCodeView.vue
<template> <div class="qrcode-demo"> <h2>二维码生成示例</h2> <!-- 示例1:基础用法 --> <section class="demo-section"> <h3>基础用法</h3> <div class="demo-box"> <QRCode value="https://deepseek.com" /> <div class="description"> 默认配置的二维码 </div> </div> </section> <!-- 示例2:自定义样式 --> <section class="demo-section"> <h3>自定义样式</h3> <div class="demo-box"> <QRCode value="https://deepseek.com" :size="250" color-dark="#2c3e50" color-light="#ecf0f1" /> <div class="description"> 自定义大小和颜色 </div> </div> </section> <!-- 示例3:带事件处理 --> <section class="demo-section"> <h3>事件处理</h3> <div class="demo-box"> <QRCode value="Event Example" :margin="10" @generated="handleGenerated" @error="handleError" /> <div :class="{ error: hasError }"> {{ statusMessage }} </div> </div> </section> <!-- 示例4:纠错级别 --> <section class="demo-section"> <h3>纠错级别</h3> <div class="demo-box"> <QRCode value="Error Correction Example" type="Q" /> <div class="description"> 使用 Q 级别纠错(约25%) </div> </div> </section> <!-- 示例5:动态内容 --> <section class="demo-section"> <h3>动态内容</h3> <div class="demo-box"> <div class="input-group"> <input v-model="dynamicValue" placeholder="输入二维码内容" > </div> <QRCode :value="dynamicValue || '请输入内容'" :size="200" /> </div> </section> </div> </template> <script setup> import { ref } from 'vue' import QRCode from '@/components/QRCode/QRCode.vue' const dynamicValue = ref('') const statusMessage = ref('') const hasError = ref(false) const handleGenerated = (dataUrl) => { statusMessage.value = '二维码生成成功!' hasError.value = false console.log('QR Code generated:', dataUrl) } const handleError = (error) => { statusMessage.value = '生成失败:' + error.message hasError.value = true console.error('QR Code generation failed:', error) } </script> <style scoped> .qrcode-demo { padding: 20px; max-width: 800px; margin: 0 auto; } h2 { color: #2c3e50; text-align: center; margin-bottom: 30px; } .demo-section { margin-bottom: 30px; padding: 20px; border: 1px solid #ebeef5; border-radius: 8px; background: white; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05); } h3 { color: #2c3e50; margin-bottom: 15px; } .demo-box { display: flex; flex-direction: column; align-items: center; gap: 15px; padding: 20px; background: #f8f9fa; border-radius: 6px; } .description { color: #666; font-size: 14px; text-align: center; } .input-group { width: 100%; max-width: 300px; margin-bottom: 15px; } .demo-input { width: 100%; padding: 8px 12px; border: 1px solid #dcdfe6; border-radius: 4px; font-size: 14px; } .demo-input:focus { outline: none; border-color: #409eff; } .status-message { padding: 8px 12px; border-radius: 4px; background: #f0f9eb; color: #67c23a; font-size: 14px; } .status-message.error { background: #fef0f0; color: #f56c6c; } @media (max-width: 768px) { .qrcode-demo { padding: 10px; } .demo-section { padding: 15px; } .demo-box { padding: 15px; } } </style>
📚测试代码正常跑通,附其他基本代码
添加路由
页面展示入口
📘编写路由 src\router\index.js
import { createRouter, createWebHistory } from 'vue-router' import RightClickMenuView from '../views/RightClickMenuView.vue' import RangePickerView from '../views/RangePickerView.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'progress', component: () => import('../views/ProgressView.vue'), }, { path: '/tabs', name: 'tabs', // route level code-splitting // this generates a separate chunk (About.[hash].js) for this route // which is lazy-loaded when the route is visited. // 标签页(Tabs) component: () => import('../views/TabsView.vue'), }, { path: '/accordion', name: 'accordion', // 折叠面板(Accordion) component: () => import('../views/AccordionView.vue'), }, { path: '/timeline', name: 'timeline', // 时间线(Timeline) component: () => import('../views/TimelineView.vue'), }, { path: '/backToTop', name: 'backToTop', component: () => import('../views/BackToTopView.vue') }, { path: '/notification', name: 'notification', component: () => import('../views/NotificationView.vue') }, { path: '/card', name: 'card', component: () => import('../views/CardView.vue') }, { path: '/infiniteScroll', name: 'infiniteScroll', component: () => import('../views/InfiniteScrollView.vue') }, { path: '/switch', name: 'switch', component: () => import('../views/SwitchView.vue') }, { path: '/sidebar', name: 'sidebar', component: () => import('../views/SidebarView.vue') }, { path: '/breadcrumbs', name: 'breadcrumbs', component: () => import('../views/BreadcrumbsView.vue') }, { path: '/masonryLayout', name: 'masonryLayout', component: () => import('../views/MasonryLayoutView.vue') }, { path: '/rating', name: 'rating', component: () => import('../views/RatingView.vue') }, { path: '/datePicker', name: 'datePicker', component: () => import('../views/DatePickerView.vue') }, { path: '/colorPicker', name: 'colorPicker', component: () => import('../views/ColorPickerView.vue') }, { path: '/rightClickMenu', name: 'rightClickMenu', component: RightClickMenuView }, { path: '/rangePicker', name: 'rangePicker', component: () => import('../views/RangePickerView.vue') }, { path: '/navbar', name: 'navbar', component: () => import('../views/NavbarView.vue') }, { path: '/formValidation', name: 'formValidation', component: () => import('../views/FormValidationView.vue') }, { path: '/copyToClipboard', name: 'copyToClipboard', component: () => import('../views/CopyToClipboardView.vue') }, { path: '/clickAnimations', name: 'clickAnimations', component: () => import('../views/ClickAnimationsView.vue') }, { path: '/thumbnailList', name: 'thumbnailList', component: () => import('../views/ThumbnailListView.vue') }, { path: '/keyboardShortcuts', name: 'keyboardShortcuts', component: () => import('../views/KeyboardShortcutsView.vue') }, { path: '/commentSystem', name: 'commentSystem', component: () => import('../views/CommentSystemView.vue') }, { path: '/qRCode', name: 'qRCode', component: () => import('../views/QRCodeView.vue') } ], }) export default router
📘编写展示入口 src\App.vue
<script setup> import { RouterLink, RouterView } from 'vue-router' import HelloWorld from './components/HelloWorld.vue' </script> <template> <header> <img alt="Vue logo" src="https://blog.csdn.net/qq_33650655/article/details/@/assets/logo.svg" /> <div class="wrapper"> <HelloWorld msg="You did it!" /> <nav> <RouterLink to="/">Progress</RouterLink> <RouterLink to="/tabs">Tabs</RouterLink> <RouterLink to="/accordion">Accordion</RouterLink> <RouterLink to="/timeline">Timeline</RouterLink> <RouterLink to="/backToTop">BackToTop</RouterLink> <RouterLink to="/notification">Notification</RouterLink> <RouterLink to="/card">Card</RouterLink> <RouterLink to="/infiniteScroll">InfiniteScroll</RouterLink> <RouterLink to="/switch">Switch</RouterLink> <RouterLink to="/sidebar">Sidebar</RouterLink> <RouterLink to="/breadcrumbs">Breadcrumbs</RouterLink> <RouterLink to="/masonryLayout">MasonryLayout</RouterLink> <RouterLink to="/rating">Rating</RouterLink> <RouterLink to="/datePicker">DatePicker</RouterLink> <RouterLink to="/colorPicker">ColorPicker</RouterLink> <RouterLink to="/rightClickMenu">RightClickMenu</RouterLink> <RouterLink to="/rangePicker">RangePicker</RouterLink> <RouterLink to="/navbar">Navbar</RouterLink> <RouterLink to="/formValidation">FormValidation</RouterLink> <RouterLink to="/copyToClipboard">CopyToClipboard</RouterLink> <RouterLink to="/clickAnimations">ClickAnimations</RouterLink> <RouterLink to="/thumbnailList">ThumbnailList</RouterLink> <RouterLink to="/keyboardShortcuts">KeyboardShortcuts</RouterLink> <RouterLink to="/commentSystem">CommentSystem</RouterLink> <RouterLink to="/qRCode">QRCode</RouterLink> </nav> </div> </header> <RouterView /> </template> <style scoped> header { line-height: 1.5; max-height: 100vh; } .logo { display: block; margin: 0 auto 2rem; } nav { width: 100%; font-size: 12px; text-align: center; margin-top: 2rem; } nav a.router-link-exact-active { color: var(--color-text); } nav a.router-link-exact-active:hover { background-color: transparent; } nav a { display: inline-block; padding: 0 1rem; border-left: 1px solid var(--color-border); } nav a:first-of-type { border: 0; } @media (min-width: 1024px) { header { display: flex; place-items: center; padding-right: calc(var(--section-gap) / 2); } .logo { margin: 0 2rem 0 0; } header .wrapper { display: flex; place-items: flex-start; flex-wrap: wrap; } nav { text-align: left; margin-left: -1rem; font-size: 1rem; padding: 1rem 0; margin-top: 1rem; } } </style>
总结
通过结合DeepSeek的强大搜索和推荐能力,以及Vue框架的响应式和组件化特性,我们成功打造了一款丝滑、智能且高度可定制的二维码生成组件。这款组件不仅能够根据用户输入动态生成二维码,还能与用户交互,提供更加个性化的体验。希望本文的介绍能够为您的Web开发带来新的灵感和思路,让您的应用在细节之处更加出彩。
本文来源于#宝码香车,由@蜜芽 整理发布。如若内容造成侵权/违法违规/事实不符,请联系本站客服处理!
该文章观点仅代表作者本人,不代表本站立场。本站不承担相关法律责任。
如若转载,请注明出处:https://www.zhanid.com/biancheng/3437.html