feat: add qrcode demo (#256)

This commit is contained in:
一万 2022-04-27 19:57:12 +08:00 committed by GitHub
parent 506bfc8087
commit 6c6d520dcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 519 additions and 18 deletions

View File

@ -79,3 +79,4 @@ menus:
hsDebounce: Debounce & Throttle
hsFormDesign: Form Design
hsBarcode: Barcode
hsQarcode: Qarcode

View File

@ -79,3 +79,4 @@ menus:
hsDebounce: 防抖节流
hsFormDesign: 表单设计器
hsBarcode: 条形码
hsQarcode: 二维码

View File

@ -56,6 +56,7 @@
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.0.13",
"qrcode": "^1.5.0",
"qs": "^6.10.1",
"resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.11",
@ -93,6 +94,7 @@
"@types/mockjs": "1.0.3",
"@types/node": "14.14.14",
"@types/nprogress": "0.2.0",
"@types/qrcode": "^1.4.2",
"@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",

131
pnpm-lock.yaml generated
View File

@ -26,6 +26,7 @@ specifiers:
"@types/mockjs": 1.0.3
"@types/node": 14.14.14
"@types/nprogress": 0.2.0
"@types/qrcode": ^1.4.2
"@types/qs": ^6.9.7
"@typescript-eslint/eslint-plugin": ^5.10.2
"@typescript-eslint/parser": ^5.10.2
@ -73,6 +74,7 @@ specifiers:
postcss-scss: ^4.0.3
prettier: ^2.5.1
pretty-quick: 3.1.1
qrcode: ^1.5.0
qs: ^6.10.1
resize-observer-polyfill: ^1.5.1
responsive-storage: ^1.0.11
@ -137,6 +139,7 @@ dependencies:
nprogress: 0.2.0
path: 0.12.7
pinia: 2.0.13_typescript@4.6.3+vue@3.2.33
qrcode: 1.5.0
qs: 6.10.3
resize-observer-polyfill: 1.5.1
responsive-storage: 1.0.11_vue@3.2.33
@ -174,6 +177,7 @@ devDependencies:
"@types/mockjs": 1.0.3
"@types/node": 14.14.14
"@types/nprogress": 0.2.0
"@types/qrcode": 1.4.2
"@types/qs": 6.9.7
"@typescript-eslint/eslint-plugin": 5.16.0_bc68a9cd5bf604202498b1a9faaf9387
"@typescript-eslint/parser": 5.16.0_eslint@8.11.0+typescript@4.6.3
@ -1493,6 +1497,15 @@ packages:
}
dev: true
/@types/qrcode/1.4.2:
resolution:
{
integrity: sha512-7uNT9L4WQTNJejHTSTdaJhfBSCN73xtXaHFyBJ8TSwiLhe4PRuTue7Iph0s2nG9R/ifUaSnGhLUOZavlBEqDWQ==
}
dependencies:
"@types/node": 14.14.14
dev: true
/@types/qs/6.9.7:
resolution:
{
@ -2476,7 +2489,6 @@ packages:
integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
}
engines: { node: ">=8" }
dev: true
/ansi-styles/3.2.1:
resolution:
@ -2495,7 +2507,6 @@ packages:
engines: { node: ">=8" }
dependencies:
color-convert: 2.0.1
dev: true
/ant-design-vue/3.2.0_vue@3.2.33:
resolution:
@ -2763,7 +2774,6 @@ packages:
integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
}
engines: { node: ">=6" }
dev: true
/camelcase/6.3.0:
resolution:
@ -2872,6 +2882,17 @@ packages:
string-width: 4.2.3
dev: true
/cliui/6.0.0:
resolution:
{
integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
}
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
dev: false
/cliui/7.0.4:
resolution:
{
@ -2914,7 +2935,6 @@ packages:
engines: { node: ">=7.0.0" }
dependencies:
color-name: 1.1.4
dev: true
/color-name/1.1.3:
resolution: { integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= }
@ -3394,7 +3414,6 @@ packages:
/decamelize/1.2.0:
resolution: { integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= }
engines: { node: ">=0.10.0" }
dev: true
/deep-is/0.1.4:
resolution:
@ -3427,6 +3446,13 @@ packages:
engines: { node: ">=0.3.1" }
dev: true
/dijkstrajs/1.0.2:
resolution:
{
integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==
}
dev: false
/dir-glob/3.0.1:
resolution:
{
@ -3603,7 +3629,13 @@ packages:
{
integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
}
dev: true
/encode-utf8/1.0.3:
resolution:
{
integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
}
dev: false
/encodeurl/1.0.2:
resolution: { integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= }
@ -4435,7 +4467,6 @@ packages:
dependencies:
locate-path: 5.0.0
path-exists: 4.0.0
dev: true
/find-up/5.0.0:
resolution:
@ -4590,7 +4621,6 @@ packages:
integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
}
engines: { node: 6.* || 8.* || >= 10.* }
dev: true
/get-intrinsic/1.1.1:
resolution:
@ -5029,7 +5059,6 @@ packages:
integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
}
engines: { node: ">=8" }
dev: true
/is-glob/4.0.3:
resolution:
@ -5373,7 +5402,6 @@ packages:
engines: { node: ">=8" }
dependencies:
p-locate: 4.1.0
dev: true
/locate-path/6.0.0:
resolution:
@ -5937,7 +5965,6 @@ packages:
engines: { node: ">=6" }
dependencies:
p-try: 2.2.0
dev: true
/p-limit/3.1.0:
resolution:
@ -5957,7 +5984,6 @@ packages:
engines: { node: ">=8" }
dependencies:
p-limit: 2.3.0
dev: true
/p-locate/5.0.0:
resolution:
@ -5985,7 +6011,6 @@ packages:
integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
}
engines: { node: ">=6" }
dev: true
/parent-module/1.0.1:
resolution:
@ -6024,7 +6049,6 @@ packages:
integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
}
engines: { node: ">=8" }
dev: true
/path-is-absolute/1.0.1:
resolution: { integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18= }
@ -6117,6 +6141,14 @@ packages:
semver-compare: 1.0.0
dev: true
/pngjs/5.0.0:
resolution:
{
integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
}
engines: { node: ">=10.13.0" }
dev: false
/popmotion/11.0.3:
resolution:
{
@ -6679,6 +6711,20 @@ packages:
engines: { node: ">=0.6.0", teleport: ">=0.2.0" }
dev: true
/qrcode/1.5.0:
resolution:
{
integrity: sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ==
}
engines: { node: ">=10.13.0" }
hasBin: true
dependencies:
dijkstrajs: 1.0.2
encode-utf8: 1.0.3
pngjs: 5.0.0
yargs: 15.4.1
dev: false
/qs/6.10.3:
resolution:
{
@ -6785,7 +6831,6 @@ packages:
/require-directory/2.1.1:
resolution: { integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I= }
engines: { node: ">=0.10.0" }
dev: true
/require-from-string/2.0.2:
resolution:
@ -6795,6 +6840,13 @@ packages:
engines: { node: ">=0.10.0" }
dev: true
/require-main-filename/2.0.0:
resolution:
{
integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
}
dev: false
/resize-observer-polyfill/1.5.1:
resolution:
{
@ -7013,6 +7065,10 @@ packages:
lru-cache: 6.0.0
dev: true
/set-blocking/2.0.0:
resolution: { integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc= }
dev: false
/shallow-equal/1.2.1:
resolution:
{
@ -7274,7 +7330,6 @@ packages:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: true
/string_decoder/1.3.0:
resolution:
@ -7305,7 +7360,6 @@ packages:
engines: { node: ">=8" }
dependencies:
ansi-regex: 5.0.1
dev: true
/strip-final-newline/2.0.0:
resolution:
@ -8119,6 +8173,10 @@ packages:
loose-envify: 1.4.0
dev: false
/which-module/2.0.0:
resolution: { integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= }
dev: false
/which/1.3.1:
resolution:
{
@ -8171,7 +8229,6 @@ packages:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
dev: true
/wrap-ansi/7.0.0:
resolution:
@ -8234,6 +8291,13 @@ packages:
xgplayer-subtitles: 1.0.22
dev: false
/y18n/4.0.3:
resolution:
{
integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
}
dev: false
/y18n/5.0.8:
resolution:
{
@ -8268,6 +8332,17 @@ packages:
engines: { node: ">= 6" }
dev: true
/yargs-parser/18.1.3:
resolution:
{
integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
}
engines: { node: ">=6" }
dependencies:
camelcase: 5.3.1
decamelize: 1.2.0
dev: false
/yargs-parser/20.2.9:
resolution:
{
@ -8284,6 +8359,26 @@ packages:
engines: { node: ">=12" }
dev: true
/yargs/15.4.1:
resolution:
{
integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
}
engines: { node: ">=8" }
dependencies:
cliui: 6.0.0
decamelize: 1.2.0
find-up: 4.1.0
get-caller-file: 2.0.5
require-directory: 2.1.1
require-main-filename: 2.0.0
set-blocking: 2.0.0
string-width: 4.2.3
which-module: 2.0.0
y18n: 4.0.3
yargs-parser: 18.1.3
dev: false
/yargs/17.4.0:
resolution:
{

View File

@ -0,0 +1,10 @@
import { App } from "vue";
import reQrcode from "./src/index";
export const ReQrcode = Object.assign(reQrcode, {
install(app: App) {
app.component(reQrcode.name, reQrcode);
}
});
export default ReQrcode;

View File

@ -0,0 +1,8 @@
.qrcode {
&--disabled {
background: rgba(255, 255, 255, 0.95);
& > div {
transform: translate(-50%, -50%);
}
}
}

View File

@ -0,0 +1,262 @@
import {
ref,
unref,
watch,
nextTick,
computed,
PropType,
defineComponent
} from "vue";
import "./index.scss";
import { isString } from "/@/utils/is";
import { cloneDeep } from "lodash-unified";
import { propTypes } from "/@/utils/propTypes";
import { IconifyIconOffline } from "../../ReIcon";
import QRCode, { QRCodeRenderersOptions } from "qrcode";
interface QrcodeLogo {
src?: string;
logoSize?: number;
bgColor?: string;
borderSize?: number;
crossOrigin?: string;
borderRadius?: number;
logoRadius?: number;
}
const props = {
// img 或者 canvas,img不支持logo嵌套
tag: propTypes.string
.validate((v: string) => ["canvas", "img"].includes(v))
.def("canvas"),
// 二维码内容
text: {
type: [String, Array] as PropType<string | Recordable[]>,
default: null
},
// qrcode.js配置项
options: {
type: Object as PropType<QRCodeRenderersOptions>,
default: (): QRCodeRenderersOptions => ({})
},
// 宽度
width: propTypes.number.def(200),
// logo
logo: {
type: [String, Object] as PropType<Partial<QrcodeLogo> | string>,
default: (): QrcodeLogo | string => ""
},
// 是否过期
disabled: propTypes.bool.def(false),
// 过期提示内容
disabledText: propTypes.string.def("")
};
export default defineComponent({
name: "epTableProBar",
props,
emits: ["done", "click", "disabled-click"],
setup(props, { emit }) {
const { toCanvas, toDataURL } = QRCode;
const loading = ref(true);
const wrapRef = ref<Nullable<HTMLCanvasElement | HTMLImageElement>>(null);
const renderText = computed(() => String(props.text));
const wrapStyle = computed(() => {
return {
width: props.width + "px",
height: props.width + "px"
};
});
const initQrcode = async () => {
await nextTick();
const options = cloneDeep(props.options || {});
if (props.tag === "canvas") {
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
options.errorCorrectionLevel =
options.errorCorrectionLevel ||
getErrorCorrectionLevel(unref(renderText));
const _width: number = await getOriginWidth(unref(renderText), options);
options.scale =
props.width === 0 ? undefined : (props.width / _width) * 4;
const canvasRef: HTMLCanvasElement = await toCanvas(
unref(wrapRef) as HTMLCanvasElement,
unref(renderText),
options
);
if (props.logo) {
const url = await createLogoCode(canvasRef);
emit("done", url);
loading.value = false;
} else {
emit("done", canvasRef.toDataURL());
loading.value = false;
}
} else {
const url = await toDataURL(renderText.value, {
errorCorrectionLevel: "H",
width: props.width,
...options
});
(unref(wrapRef) as HTMLImageElement).src = url;
emit("done", url);
loading.value = false;
}
};
watch(
() => renderText.value,
val => {
if (!val) return;
initQrcode();
},
{
deep: true,
immediate: true
}
);
const createLogoCode = (canvasRef: HTMLCanvasElement) => {
const canvasWidth = canvasRef.width;
const logoOptions: QrcodeLogo = Object.assign(
{
logoSize: 0.15,
bgColor: "#ffffff",
borderSize: 0.05,
crossOrigin: "anonymous",
borderRadius: 8,
logoRadius: 0
},
isString(props.logo) ? {} : props.logo
);
const {
logoSize = 0.15,
bgColor = "#ffffff",
borderSize = 0.05,
crossOrigin = "anonymous",
borderRadius = 8,
logoRadius = 0
} = logoOptions;
const logoSrc = isString(props.logo) ? props.logo : props.logo.src;
const logoWidth = canvasWidth * logoSize;
const logoXY = (canvasWidth * (1 - logoSize)) / 2;
const logoBgWidth = canvasWidth * (logoSize + borderSize);
const logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2;
const ctx = canvasRef.getContext("2d");
if (!ctx) return;
// logo 底色
canvasRoundRect(ctx)(
logoBgXY,
logoBgXY,
logoBgWidth,
logoBgWidth,
borderRadius
);
ctx.fillStyle = bgColor;
ctx.fill();
// logo
const image = new Image();
if (crossOrigin || logoRadius) {
image.setAttribute("crossOrigin", crossOrigin);
}
(image as any).src = logoSrc;
// 使用image绘制可以避免某些跨域情况
const drawLogoWithImage = (image: HTMLImageElement) => {
ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
};
// 使用canvas绘制以获得更多的功能
const drawLogoWithCanvas = (image: HTMLImageElement) => {
const canvasImage = document.createElement("canvas");
canvasImage.width = logoXY + logoWidth;
canvasImage.height = logoXY + logoWidth;
const imageCanvas = canvasImage.getContext("2d");
if (!imageCanvas || !ctx) return;
imageCanvas.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius);
if (!ctx) return;
const fillStyle = ctx.createPattern(canvasImage, "no-repeat");
if (fillStyle) {
ctx.fillStyle = fillStyle;
ctx.fill();
}
};
// 将 logo绘制到 canvas上
return new Promise((resolve: any) => {
image.onload = () => {
logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image);
resolve(canvasRef.toDataURL());
};
});
};
// 得到原QrCode的大小以便缩放得到正确的QrCode大小
const getOriginWidth = async (
content: string,
options: QRCodeRenderersOptions
) => {
const _canvas = document.createElement("canvas");
await toCanvas(_canvas, content, options);
return _canvas.width;
};
// 对于内容少的QrCode增大容错率
const getErrorCorrectionLevel = (content: string) => {
if (content.length > 36) {
return "M";
} else if (content.length > 16) {
return "Q";
} else {
return "H";
}
};
// 用于绘制圆角
const canvasRoundRect = (ctx: CanvasRenderingContext2D) => {
return (x: number, y: number, w: number, h: number, r: number) => {
const minSize = Math.min(w, h);
if (r > minSize / 2) {
r = minSize / 2;
}
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
return ctx;
};
};
const clickCode = () => {
emit("click");
};
const disabledClick = () => {
emit("disabled-click");
};
return () => (
<>
<div
v-loading={unref(loading)}
class="qrcode relative inline-block"
style={unref(wrapStyle)}
>
{props.tag === "canvas" ? (
<canvas ref={wrapRef} onClick={clickCode}></canvas>
) : (
<img ref={wrapRef} onClick={clickCode}></img>
)}
{props.disabled && (
<div
class="qrcode--disabled absolute top-0 left-0 flex w-full h-full items-center justify-center"
onClick={disabledClick}
>
<div class="absolute top-[50%] left-[50%] font-bold">
<IconifyIconOffline
class="cursor-pointer outline-none"
icon="refresh-right"
width="30"
color="var(--el-color-primary)"
/>
<div>{props.disabledText}</div>
</div>
</div>
)}
</div>
</>
);
}
});

View File

@ -110,6 +110,15 @@ const ableRouter = {
title: $t("menus.hsBarcode"),
i18n: true
}
},
{
path: "/able/qrcode",
name: "reQarcode",
component: () => import("/@/views/able/qrcode.vue"),
meta: {
title: $t("menus.hsQarcode"),
i18n: true
}
}
]
};

113
src/views/able/qrcode.vue Normal file
View File

@ -0,0 +1,113 @@
<script setup lang="ts">
import { ref, unref } from "vue";
import { ElMessage } from "element-plus";
import avatars from "/@/assets/avatars.jpg";
import ReQrcode from "/@/components/ReQrcode";
const qrcodeText = "vue-pure-admin";
const asyncTitle = ref("");
setTimeout(() => {
asyncTitle.value = unref(qrcodeText);
}, 3000);
const codeClick = () => {
ElMessage.info("点击事件");
};
const disabledClick = () => {
ElMessage.info("失效");
};
</script>
<template>
<div>
<el-card>
<template #header>
<div class="font-medium">
二维码基于<el-link
href="https://github.com/soldair/node-qrcode"
target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0"
>qrcode</el-link
>生成
</div>
</template>
<el-row :gutter="20" justify="space-between">
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">基础用法</div>
<ReQrcode :text="qrcodeText" />
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">img标签</div>
<ReQrcode :text="qrcodeText" tag="img" />
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">样式配置</div>
<ReQrcode
:text="qrcodeText"
:options="{
color: {
dark: '#55D187',
light: '#2d8cf0'
}
}"
/>
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">点击事件</div>
<ReQrcode :text="qrcodeText" @click="codeClick" />
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">异步内容</div>
<ReQrcode :text="asyncTitle" />
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">失效</div>
<ReQrcode
:text="qrcodeText"
disabled
@disabled-click="disabledClick"
/>
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">logo配置</div>
<ReQrcode :text="qrcodeText" :logo="avatars" />
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">logo样式</div>
<ReQrcode
:text="qrcodeText"
:logo="{
src: avatars,
logoSize: 0.2,
borderSize: 0.05,
borderRadius: 50,
bgColor: 'blue'
}"
/>
</el-card>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px text-center">
<div class="font-bold">大小配置</div>
<ReQrcode :text="qrcodeText" :width="100" />
</el-card>
</el-col>
</el-row>
</el-card>
</div>
</template>