下载二维码功能
This commit is contained in:
parent
7cdfb9c7b6
commit
e65263cffb
|
@ -1,215 +1,233 @@
|
|||
<template>
|
||||
<div ref="qrcodeRef">
|
||||
<QRCodeVue3
|
||||
:margin="margin"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:value="value"
|
||||
:qrOptions="qrOptions"
|
||||
:image="image"
|
||||
:imageOptions="imageOptions"
|
||||
:dotsOptions="dotsOptions"
|
||||
:backgroundOptions="backgroundOptions"
|
||||
:cornersSquareOptions="cornersSquareOptions"
|
||||
:cornersDotOptions="cornersDotOptions"
|
||||
:key="key"
|
||||
/>
|
||||
<div class="qrcode-container">
|
||||
<!-- 直接绑定ref到QRCodeVue3组件 -->
|
||||
<QRCodeVue3 ref="qrcodeInstance" render-as="canvas" :margin="margin" :width="width" :height="height" :value="value"
|
||||
:qrOptions="qrOptions" :image="image" :imageOptions="imageOptions" :dotsOptions="dotsOptions"
|
||||
:backgroundOptions="backgroundOptions" :cornersSquareOptions="cornersSquareOptions"
|
||||
:cornersDotOptions="cornersDotOptions" :key="key" />
|
||||
<button @click="downloadQRCode" :disabled="isDownloading" class="download-btn">
|
||||
{{ isDownloading ? '生成中...' : '下载二维码' }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, reactive, toRefs, PropType, ref} from "vue";
|
||||
import QRCodeVue3 from "qrcode-vue3";
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, toRefs, nextTick } from 'vue'
|
||||
import QRCodeVue3 from 'qrcode-vue3'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: "Qrcode",
|
||||
components: {
|
||||
QRCodeVue3,
|
||||
// 定义组件实例类型
|
||||
interface QRCodeExpose {
|
||||
$el: HTMLCanvasElement
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
key: {
|
||||
type: String as PropType<string>,
|
||||
default: "0",
|
||||
},
|
||||
props: {
|
||||
// 二维码宽度
|
||||
key: {
|
||||
type: String as PropType<string>,
|
||||
default: 0,
|
||||
},
|
||||
// 二维码宽度
|
||||
width: {
|
||||
type: String as PropType<string>,
|
||||
default: 600,
|
||||
},
|
||||
// 二维码高度
|
||||
height: {
|
||||
type: String as PropType<string>,
|
||||
default: 600,
|
||||
},
|
||||
// 二维码内容,网址
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: "https://www.focusnu.com/devops",
|
||||
},
|
||||
// 二维码图像的外边距
|
||||
margin: {
|
||||
type: String as PropType<string>,
|
||||
default:6,
|
||||
},
|
||||
//二维码背景色
|
||||
backgroundColor: {
|
||||
type: String as PropType<string>,
|
||||
default: "white",
|
||||
},
|
||||
//二维码中间的logo图片
|
||||
logo: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
// 隐藏图片背后有点
|
||||
hideLogoDots: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
//二维码中间的logo大小
|
||||
logoSize: {
|
||||
type: String as PropType<string>,
|
||||
default: 0.5,
|
||||
},
|
||||
//二维码中间的logo外边距
|
||||
logoMargin: {
|
||||
type: String as PropType<string>,
|
||||
default: 5,
|
||||
},
|
||||
//二维码点配置
|
||||
dotsOptions: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
type: "rounded", // 二维码样式 square | dots | rounded | extra-rounded | classy | classy-rounded
|
||||
color: "#0d2a56", //不设置渐变色时显示的颜色
|
||||
// 渐变色,优先级高于默认color颜色
|
||||
gradient: {
|
||||
type: "radial", // linear线性渐变 | radial径向渐变
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "#21a3fa",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#0d2a56",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
/**
|
||||
* 角落广场配置
|
||||
*/
|
||||
cornersSquareOptions: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
type: "extra-rounded", // none | square | dot | extra-rounded
|
||||
color: "#0d2a56",//不设置渐变色时显示的颜色
|
||||
// 渐变色
|
||||
gradient: {
|
||||
type: "linear",
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "#0d2a56",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#21a3fa",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
/**
|
||||
* 角落点配置
|
||||
*/
|
||||
cornersDotOptions: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
type: "dot", // none | square | dot
|
||||
color: "#0d2a56",//不设置渐变色时显示的颜色
|
||||
// 渐变色
|
||||
gradient: {
|
||||
type: "linear",
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "#0d2a56",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#21a3fa",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
width: {
|
||||
type: String as PropType<string>,
|
||||
default: "600",
|
||||
},
|
||||
setup(props) {
|
||||
console.log(props);
|
||||
const qrcodeRef = ref(null);
|
||||
const data = reactive({
|
||||
key: props.key,//key值变化时自动刷新
|
||||
/**
|
||||
* 基础配置
|
||||
* https://qr-code-styling.com
|
||||
*/
|
||||
width: props.width, // 二维码宽度
|
||||
height: props.height, // 二维码高度
|
||||
value: props.value, // 二维码内容
|
||||
margin: props.margin, // 二维码图像的外边距
|
||||
/**
|
||||
* 背景配置
|
||||
*/
|
||||
backgroundOptions: {
|
||||
//二维码背景色
|
||||
color: props.backgroundColor,
|
||||
},
|
||||
|
||||
/**
|
||||
* 二维码配置
|
||||
*/
|
||||
qrOptions: {
|
||||
typeNumber: "0", // 类型编号 0 - 40
|
||||
mode: "Byte", // 模式 Numeric | Alphanumeric | Byte | Kanji
|
||||
errorCorrectionLevel: "Q", // 纠错等级 L | M | Q | H 'L'(低)、'M'(中)、'Q'(高)、'H'(非常高)
|
||||
},
|
||||
|
||||
/**
|
||||
* 图像配置(中心图片)
|
||||
*/
|
||||
image: props.logo, // 二维码中心的图片
|
||||
imageOptions: {
|
||||
hideBackgroundDots: props.hideLogoDots, // 隐藏图片背后有点
|
||||
imageSize: props.logoSize,
|
||||
margin: props.logoMargin,
|
||||
crossOrigin: "anonymous", // anonymous | use-credentials
|
||||
},
|
||||
/**
|
||||
* 二维码点配置
|
||||
*/
|
||||
dotsOptions: props.dotsOptions,
|
||||
/**
|
||||
* 角落广场配置
|
||||
*/
|
||||
cornersSquareOptions: props.cornersSquareOptions,
|
||||
/**
|
||||
* 角落点配置
|
||||
*/
|
||||
cornersDotOptions: props.cornersDotOptions,
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(data),
|
||||
qrcodeRef
|
||||
};
|
||||
height: {
|
||||
type: String as PropType<string>,
|
||||
default: "600",
|
||||
},
|
||||
});
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: "https://www.focusnu.com/devops",
|
||||
},
|
||||
margin: {
|
||||
type: String as PropType<string>,
|
||||
default: "6",
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String as PropType<string>,
|
||||
default: "white",
|
||||
},
|
||||
logo: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
hideLogoDots: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
logoSize: {
|
||||
type: String as PropType<string>,
|
||||
default: "0.5",
|
||||
},
|
||||
logoMargin: {
|
||||
type: String as PropType<string>,
|
||||
default: "5",
|
||||
},
|
||||
dotsOptions: {
|
||||
type: Object as PropType<{
|
||||
type: string;
|
||||
color: string;
|
||||
gradient?: {
|
||||
type: string;
|
||||
rotation: number;
|
||||
colorStops: Array<{ offset: number; color: string }>;
|
||||
};
|
||||
}>,
|
||||
default: () => ({
|
||||
type: "rounded",
|
||||
color: "#0d2a56",
|
||||
gradient: {
|
||||
type: "radial",
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: "#21a3fa" },
|
||||
{ offset: 1, color: "#0d2a56" }
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
cornersSquareOptions: {
|
||||
type: Object as PropType<{
|
||||
type: string;
|
||||
color: string;
|
||||
gradient?: {
|
||||
type: string;
|
||||
rotation: number;
|
||||
colorStops: Array<{ offset: number; color: string }>;
|
||||
};
|
||||
}>,
|
||||
default: () => ({
|
||||
type: "extra-rounded",
|
||||
color: "#0d2a56",
|
||||
gradient: {
|
||||
type: "linear",
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: "#0d2a56" },
|
||||
{ offset: 1, color: "#21a3fa" }
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
cornersDotOptions: {
|
||||
type: Object as PropType<{
|
||||
type: string;
|
||||
color: string;
|
||||
gradient?: {
|
||||
type: string;
|
||||
rotation: number;
|
||||
colorStops: Array<{ offset: number; color: string }>;
|
||||
};
|
||||
}>,
|
||||
default: () => ({
|
||||
type: "dot",
|
||||
color: "#0d2a56",
|
||||
gradient: {
|
||||
type: "linear",
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: "#0d2a56" },
|
||||
{ offset: 1, color: "#21a3fa" }
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const qrcodeInstance = ref<QRCodeExpose | null>(null)
|
||||
const isDownloading = ref(false)
|
||||
|
||||
const data = reactive({
|
||||
key: props.key,
|
||||
width: props.width,
|
||||
height: props.height,
|
||||
value: props.value,
|
||||
margin: props.margin,
|
||||
backgroundOptions: {
|
||||
color: props.backgroundColor,
|
||||
},
|
||||
qrOptions: {
|
||||
typeNumber: "0",
|
||||
mode: "Byte",
|
||||
errorCorrectionLevel: "Q",
|
||||
},
|
||||
image: props.logo,
|
||||
imageOptions: {
|
||||
hideBackgroundDots: props.hideLogoDots,
|
||||
imageSize: props.logoSize,
|
||||
margin: props.logoMargin,
|
||||
crossOrigin: "anonymous",
|
||||
},
|
||||
dotsOptions: props.dotsOptions,
|
||||
cornersSquareOptions: props.cornersSquareOptions,
|
||||
cornersDotOptions: props.cornersDotOptions,
|
||||
})
|
||||
|
||||
const {
|
||||
key,
|
||||
width,
|
||||
height,
|
||||
value,
|
||||
margin,
|
||||
backgroundOptions,
|
||||
qrOptions,
|
||||
image,
|
||||
imageOptions,
|
||||
dotsOptions,
|
||||
cornersSquareOptions,
|
||||
cornersDotOptions
|
||||
} = toRefs(data)
|
||||
|
||||
const downloadQRCode = async () => {
|
||||
isDownloading.value = true
|
||||
|
||||
await nextTick() // 确保 DOM 渲染完成
|
||||
|
||||
// 从组件根元素里查找第一个 img
|
||||
const imgEl = qrcodeInstance.value?.$el?.querySelector?.('img')
|
||||
|
||||
if (imgEl && imgEl.src) {
|
||||
const imgSrc = imgEl.src
|
||||
const link = document.createElement('a')
|
||||
link.href = imgSrc
|
||||
link.download = 'qrcode.png'
|
||||
link.click()
|
||||
} else {
|
||||
console.error('未找到二维码 img 或 src 为空')
|
||||
}
|
||||
|
||||
isDownloading.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.qrcode-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
padding: 8px 16px;
|
||||
background-color: #42b983;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.download-btn:hover {
|
||||
background-color: #3aa876;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.download-btn:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue