下载二维码功能
This commit is contained in:
parent
7cdfb9c7b6
commit
e65263cffb
|
@ -1,215 +1,233 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="qrcodeRef">
|
<div class="qrcode-container">
|
||||||
<QRCodeVue3
|
<!-- 直接绑定ref到QRCodeVue3组件 -->
|
||||||
:margin="margin"
|
<QRCodeVue3 ref="qrcodeInstance" render-as="canvas" :margin="margin" :width="width" :height="height" :value="value"
|
||||||
:width="width"
|
:qrOptions="qrOptions" :image="image" :imageOptions="imageOptions" :dotsOptions="dotsOptions"
|
||||||
:height="height"
|
:backgroundOptions="backgroundOptions" :cornersSquareOptions="cornersSquareOptions"
|
||||||
:value="value"
|
:cornersDotOptions="cornersDotOptions" :key="key" />
|
||||||
:qrOptions="qrOptions"
|
<button @click="downloadQRCode" :disabled="isDownloading" class="download-btn">
|
||||||
:image="image"
|
{{ isDownloading ? '生成中...' : '下载二维码' }}
|
||||||
:imageOptions="imageOptions"
|
</button>
|
||||||
:dotsOptions="dotsOptions"
|
|
||||||
:backgroundOptions="backgroundOptions"
|
|
||||||
:cornersSquareOptions="cornersSquareOptions"
|
|
||||||
:cornersDotOptions="cornersDotOptions"
|
|
||||||
:key="key"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import {defineComponent, reactive, toRefs, PropType, ref} from "vue";
|
import { ref, reactive, toRefs, nextTick } from 'vue'
|
||||||
import QRCodeVue3 from "qrcode-vue3";
|
import QRCodeVue3 from 'qrcode-vue3'
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
// 定义组件实例类型
|
||||||
name: "Qrcode",
|
interface QRCodeExpose {
|
||||||
components: {
|
$el: HTMLCanvasElement
|
||||||
QRCodeVue3,
|
}
|
||||||
},
|
|
||||||
props: {
|
const props = defineProps({
|
||||||
// 二维码宽度
|
|
||||||
key: {
|
key: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: 0,
|
default: "0",
|
||||||
},
|
},
|
||||||
// 二维码宽度
|
|
||||||
width: {
|
width: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: 600,
|
default: "600",
|
||||||
},
|
},
|
||||||
// 二维码高度
|
|
||||||
height: {
|
height: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: 600,
|
default: "600",
|
||||||
},
|
},
|
||||||
// 二维码内容,网址
|
|
||||||
value: {
|
value: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: "https://www.focusnu.com/devops",
|
default: "https://www.focusnu.com/devops",
|
||||||
},
|
},
|
||||||
// 二维码图像的外边距
|
|
||||||
margin: {
|
margin: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default:6,
|
default: "6",
|
||||||
},
|
},
|
||||||
//二维码背景色
|
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: "white",
|
default: "white",
|
||||||
},
|
},
|
||||||
//二维码中间的logo图片
|
|
||||||
logo: {
|
logo: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
// 隐藏图片背后有点
|
|
||||||
hideLogoDots: {
|
hideLogoDots: {
|
||||||
type: Boolean as PropType<boolean>,
|
type: Boolean as PropType<boolean>,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
//二维码中间的logo大小
|
|
||||||
logoSize: {
|
logoSize: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: 0.5,
|
default: "0.5",
|
||||||
},
|
},
|
||||||
//二维码中间的logo外边距
|
|
||||||
logoMargin: {
|
logoMargin: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: 5,
|
default: "5",
|
||||||
},
|
},
|
||||||
//二维码点配置
|
|
||||||
dotsOptions: {
|
dotsOptions: {
|
||||||
type: Object,
|
type: Object as PropType<{
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
gradient?: {
|
||||||
|
type: string;
|
||||||
|
rotation: number;
|
||||||
|
colorStops: Array<{ offset: number; color: string }>;
|
||||||
|
};
|
||||||
|
}>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
type: "rounded", // 二维码样式 square | dots | rounded | extra-rounded | classy | classy-rounded
|
type: "rounded",
|
||||||
color: "#0d2a56", //不设置渐变色时显示的颜色
|
color: "#0d2a56",
|
||||||
// 渐变色,优先级高于默认color颜色
|
|
||||||
gradient: {
|
gradient: {
|
||||||
type: "radial", // linear线性渐变 | radial径向渐变
|
type: "radial",
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{
|
{ offset: 0, color: "#21a3fa" },
|
||||||
offset: 0,
|
{ offset: 1, color: "#0d2a56" }
|
||||||
color: "#21a3fa",
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: "#0d2a56",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 角落广场配置
|
|
||||||
*/
|
|
||||||
cornersSquareOptions: {
|
cornersSquareOptions: {
|
||||||
type: Object,
|
type: Object as PropType<{
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
gradient?: {
|
||||||
|
type: string;
|
||||||
|
rotation: number;
|
||||||
|
colorStops: Array<{ offset: number; color: string }>;
|
||||||
|
};
|
||||||
|
}>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
type: "extra-rounded", // none | square | dot | extra-rounded
|
type: "extra-rounded",
|
||||||
color: "#0d2a56",//不设置渐变色时显示的颜色
|
color: "#0d2a56",
|
||||||
// 渐变色
|
|
||||||
gradient: {
|
gradient: {
|
||||||
type: "linear",
|
type: "linear",
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{
|
{ offset: 0, color: "#0d2a56" },
|
||||||
offset: 0,
|
{ offset: 1, color: "#21a3fa" }
|
||||||
color: "#0d2a56",
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: "#21a3fa",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 角落点配置
|
|
||||||
*/
|
|
||||||
cornersDotOptions: {
|
cornersDotOptions: {
|
||||||
type: Object,
|
type: Object as PropType<{
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
gradient?: {
|
||||||
|
type: string;
|
||||||
|
rotation: number;
|
||||||
|
colorStops: Array<{ offset: number; color: string }>;
|
||||||
|
};
|
||||||
|
}>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
type: "dot", // none | square | dot
|
type: "dot",
|
||||||
color: "#0d2a56",//不设置渐变色时显示的颜色
|
color: "#0d2a56",
|
||||||
// 渐变色
|
|
||||||
gradient: {
|
gradient: {
|
||||||
type: "linear",
|
type: "linear",
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{
|
{ offset: 0, color: "#0d2a56" },
|
||||||
offset: 0,
|
{ offset: 1, color: "#21a3fa" }
|
||||||
color: "#0d2a56",
|
]
|
||||||
},
|
}
|
||||||
{
|
})
|
||||||
offset: 1,
|
}
|
||||||
color: "#21a3fa",
|
})
|
||||||
},
|
|
||||||
],
|
const qrcodeInstance = ref<QRCodeExpose | null>(null)
|
||||||
},
|
const isDownloading = ref(false)
|
||||||
}),
|
|
||||||
},
|
const data = reactive({
|
||||||
},
|
key: props.key,
|
||||||
setup(props) {
|
width: props.width,
|
||||||
console.log(props);
|
height: props.height,
|
||||||
const qrcodeRef = ref(null);
|
value: props.value,
|
||||||
const data = reactive({
|
margin: props.margin,
|
||||||
key: props.key,//key值变化时自动刷新
|
|
||||||
/**
|
|
||||||
* 基础配置
|
|
||||||
* https://qr-code-styling.com
|
|
||||||
*/
|
|
||||||
width: props.width, // 二维码宽度
|
|
||||||
height: props.height, // 二维码高度
|
|
||||||
value: props.value, // 二维码内容
|
|
||||||
margin: props.margin, // 二维码图像的外边距
|
|
||||||
/**
|
|
||||||
* 背景配置
|
|
||||||
*/
|
|
||||||
backgroundOptions: {
|
backgroundOptions: {
|
||||||
//二维码背景色
|
|
||||||
color: props.backgroundColor,
|
color: props.backgroundColor,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 二维码配置
|
|
||||||
*/
|
|
||||||
qrOptions: {
|
qrOptions: {
|
||||||
typeNumber: "0", // 类型编号 0 - 40
|
typeNumber: "0",
|
||||||
mode: "Byte", // 模式 Numeric | Alphanumeric | Byte | Kanji
|
mode: "Byte",
|
||||||
errorCorrectionLevel: "Q", // 纠错等级 L | M | Q | H 'L'(低)、'M'(中)、'Q'(高)、'H'(非常高)
|
errorCorrectionLevel: "Q",
|
||||||
},
|
},
|
||||||
|
image: props.logo,
|
||||||
/**
|
|
||||||
* 图像配置(中心图片)
|
|
||||||
*/
|
|
||||||
image: props.logo, // 二维码中心的图片
|
|
||||||
imageOptions: {
|
imageOptions: {
|
||||||
hideBackgroundDots: props.hideLogoDots, // 隐藏图片背后有点
|
hideBackgroundDots: props.hideLogoDots,
|
||||||
imageSize: props.logoSize,
|
imageSize: props.logoSize,
|
||||||
margin: props.logoMargin,
|
margin: props.logoMargin,
|
||||||
crossOrigin: "anonymous", // anonymous | use-credentials
|
crossOrigin: "anonymous",
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 二维码点配置
|
|
||||||
*/
|
|
||||||
dotsOptions: props.dotsOptions,
|
dotsOptions: props.dotsOptions,
|
||||||
/**
|
|
||||||
* 角落广场配置
|
|
||||||
*/
|
|
||||||
cornersSquareOptions: props.cornersSquareOptions,
|
cornersSquareOptions: props.cornersSquareOptions,
|
||||||
/**
|
|
||||||
* 角落点配置
|
|
||||||
*/
|
|
||||||
cornersDotOptions: props.cornersDotOptions,
|
cornersDotOptions: props.cornersDotOptions,
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
const {
|
||||||
...toRefs(data),
|
key,
|
||||||
qrcodeRef
|
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>
|
</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