From 3394dfea5aef160d1035fae8be18e1940355f5d7 Mon Sep 17 00:00:00 2001 From: ZhouWei <1244620067@qq.com> Date: Thu, 15 Jun 2023 23:47:38 +0800 Subject: [PATCH 1/3] fix: Synchronize QR code demonstration and add SVG --- .../qrcode/{QRCodeCanvas.tsx => QRCode.tsx} | 90 ++++++++++++++++++- .../__tests__/__snapshots__/demo.test.js.snap | 23 +++++ components/qrcode/demo/base.vue | 16 +++- components/qrcode/demo/customColor.vue | 15 +++- components/qrcode/demo/customType.vue | 22 +++++ components/qrcode/demo/index.vue | 14 ++- components/qrcode/index.en-US.md | 2 + components/qrcode/index.tsx | 13 ++- components/qrcode/index.zh-CN.md | 2 + components/qrcode/interface.ts | 2 + 10 files changed, 190 insertions(+), 9 deletions(-) rename components/qrcode/{QRCodeCanvas.tsx => QRCode.tsx} (77%) create mode 100644 components/qrcode/demo/customType.vue diff --git a/components/qrcode/QRCodeCanvas.tsx b/components/qrcode/QRCode.tsx similarity index 77% rename from components/qrcode/QRCodeCanvas.tsx rename to components/qrcode/QRCode.tsx index 787c89946d..d5c1fa1f76 100644 --- a/components/qrcode/QRCodeCanvas.tsx +++ b/components/qrcode/QRCode.tsx @@ -1,5 +1,5 @@ import type { CSSProperties } from 'vue'; -import { defineComponent, shallowRef, watch, computed, watchEffect } from 'vue'; +import { defineComponent, shallowRef, watch, computed, watchEffect, toRefs, unref } from 'vue'; import type { ImageSettings } from './interface'; import { qrProps } from './interface'; @@ -265,3 +265,91 @@ export const QRCodeCanvas = defineComponent({ }; }, }); + +export const QRCodeSVG = defineComponent({ + name: 'QRCodeSVG', + inheritAttrs: false, + props: { + ...qrProps(), + color: String, + level: String, + bgColor: String, + fgColor: String, + marginSize: Number, + title: String, + }, + setup(props) { + const { + value, + size = DEFAULT_SIZE, + level = DEFAULT_LEVEL, + bgColor = DEFAULT_BGCOLOR, + fgColor = DEFAULT_FGCOLOR, + includeMargin = DEFAULT_INCLUDEMARGIN, + title, + marginSize, + imageSettings, + } = toRefs(props); + const _svg = shallowRef(null); + + let cells = qrcodegen.QrCode.encodeText( + unref(value), + ERROR_LEVEL_MAP[unref(level)], + ).getModules(); + + const margin = getMarginSize(unref(includeMargin), unref(marginSize)); + const numCells = cells.length + margin * 2; + const calculatedImageSettings = getImageSettings( + cells, + unref(size), + margin, + unref(imageSettings), + ); + + let image = null; + if (imageSettings != null && calculatedImageSettings != null) { + if (calculatedImageSettings.excavation != null) { + cells = excavateModules(cells, calculatedImageSettings.excavation); + } + + image = ( + + ); + } + + // Drawing strategy: instead of a rect per module, we're going to create a + // single path for the dark modules and layer that on top of a light rect, + // for a total of 2 DOM nodes. We pay a bit more in string concat but that's + // way faster than DOM ops. + // For level 1, 441 nodes -> 2 + // For level 40, 31329 -> 2 + const fgPath = generatePath(cells, margin); + + return () => { + return ( + + {!!title && {title}} + + + {image} + + ); + }; + }, +}); diff --git a/components/qrcode/__tests__/__snapshots__/demo.test.js.snap b/components/qrcode/__tests__/__snapshots__/demo.test.js.snap index 6f5c081d65..e056575998 100644 --- a/components/qrcode/__tests__/__snapshots__/demo.test.js.snap +++ b/components/qrcode/__tests__/__snapshots__/demo.test.js.snap @@ -39,6 +39,29 @@ exports[`renders ./components/qrcode/demo/customSize.vue correctly 1`] = ` `; +exports[`renders ./components/qrcode/demo/customType.vue correctly 1`] = ` +
+
+
+ + +
+
+ +
+
+ + + + + + +
+
+ +
+`; + exports[`renders ./components/qrcode/demo/download.vue correctly 1`] = `
diff --git a/components/qrcode/demo/base.vue b/components/qrcode/demo/base.vue index 39bf6f4384..20b498af74 100644 --- a/components/qrcode/demo/base.vue +++ b/components/qrcode/demo/base.vue @@ -15,5 +15,19 @@ Basic Usage. + + diff --git a/components/qrcode/demo/customColor.vue b/components/qrcode/demo/customColor.vue index 00ba76498c..2ac1a4ed17 100644 --- a/components/qrcode/demo/customColor.vue +++ b/components/qrcode/demo/customColor.vue @@ -16,7 +16,18 @@ Custom Color. + + diff --git a/components/qrcode/demo/customType.vue b/components/qrcode/demo/customType.vue new file mode 100644 index 0000000000..13fb7a0ac8 --- /dev/null +++ b/components/qrcode/demo/customType.vue @@ -0,0 +1,22 @@ + +--- +order: 4 +title: + zh-CN: 自定义渲染类型 + en-US: Custom Render Type +--- + +## zh-CN + +通过设置 `type` 自定义渲染结果,提供 `canvas` 和 `svg` 两个选项。 + +## en-US +Customize the rendering results by `type`, provide options `canvas` and `svg`. + + + diff --git a/components/qrcode/demo/index.vue b/components/qrcode/demo/index.vue index 41d7536aca..11f7d365cf 100644 --- a/components/qrcode/demo/index.vue +++ b/components/qrcode/demo/index.vue @@ -3,6 +3,7 @@ + @@ -23,9 +24,20 @@ import CustomColor from './customColor.vue'; import Download from './download.vue'; import ErrorLevel from './errorLevel.vue'; import Popover from './popover.vue'; +import CustomType from './customType.vue'; export default defineComponent({ - components: { Base, Icon, Status, CustomSize, CustomColor, Download, ErrorLevel, Popover }, + components: { + Base, + Icon, + Status, + CustomSize, + CustomColor, + Download, + ErrorLevel, + Popover, + CustomType, + }, category: 'Components', subtitle: '二维码', type: 'Data Display', diff --git a/components/qrcode/index.en-US.md b/components/qrcode/index.en-US.md index b631f509b2..1545d4c1b2 100644 --- a/components/qrcode/index.en-US.md +++ b/components/qrcode/index.en-US.md @@ -15,10 +15,12 @@ Used when the link needs to be converted into a QR Code. | Property | Description | Type | Default | | --- | --- | --- | --- | | value | scanned link | string | - | +| type | render type | `'canvas'` \| `'svg'` | `canvas` | | icon | include image url (only image link are supported) | string | - | | size | QRCode size | number | 128 | | iconSize | include image size | number | 32 | | color | QRCode Color | string | `#000` | +| bgColor | QRCode Background Color | string | `transparent` | | bordered | Whether has border style | boolean | `true` | | errorLevel | Error Code Level | `'L'` \| `'M'` \| `'Q'` \| `'H'` | `'M'` | | status | QRCode status | `active` \| `expired` \| `loading ` | `active` | diff --git a/components/qrcode/index.tsx b/components/qrcode/index.tsx index 16a14aacca..d284e72088 100644 --- a/components/qrcode/index.tsx +++ b/components/qrcode/index.tsx @@ -8,7 +8,7 @@ import Spin from '../spin'; import Button from '../button'; import { ReloadOutlined } from '@ant-design/icons-vue'; import { useToken } from '../theme/internal'; -import { QRCodeCanvas } from './QRCodeCanvas'; +import { QRCodeCanvas, QRCodeSVG } from './QRCode'; import warning from '../_util/warning'; import { qrcodeProps } from './interface'; @@ -43,6 +43,7 @@ const QRCode = defineComponent({ size = 160, iconSize = 40, color = '#000', + bgColor = 'transparent', errorLevel = 'M', } = props; const imageSettings: QRCodeProps['imageSettings'] = { @@ -57,7 +58,7 @@ const QRCode = defineComponent({ value, size: size - (token.value.paddingSM + token.value.lineWidth) * 2, level: errorLevel, - bgColor: 'transparent', + bgColor, fgColor: color, imageSettings: icon ? imageSettings : undefined, }; @@ -69,7 +70,7 @@ const QRCode = defineComponent({ {...attrs} style={[ attrs.style as CSSProperties, - { width: props.size + 'px', height: props.size + 'px' }, + { width: props.size + 'px', height: props.size + 'px', backgroundColor: props.bgColor }, ]} class={[ hashId.value, @@ -96,7 +97,11 @@ const QRCode = defineComponent({ )}
)} - + {props.type === 'canvas' ? ( + + ) : ( + + )} , ); }; diff --git a/components/qrcode/index.zh-CN.md b/components/qrcode/index.zh-CN.md index 53d99a8d18..8d266d6375 100644 --- a/components/qrcode/index.zh-CN.md +++ b/components/qrcode/index.zh-CN.md @@ -16,10 +16,12 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*M4PBTZ_n9OgAAA | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | value | 扫描后的地址 | string | - | +| type | 渲染类型 | `'canvas'` \| `'svg'` | `canvas` | | icon | 二维码中图片的地址(目前只支持图片地址) | string | - | | size | 二维码大小 | number | 160 | | iconSize | 二维码中图片的大小 | number | 40 | | color | 二维码颜色 | string | `#000` | +| bgColor | 二维码背景颜色 | string | `transparent` | | bordered | 是否有边框 | boolean | `true` | | errorLevel | 二维码纠错等级 | `'L'` \| `'M'` \| `'Q'` \| `'H'` | `'M'` | | status | 二维码状态 | `active` \| `expired` \| `loading ` | `active` | diff --git a/components/qrcode/interface.ts b/components/qrcode/interface.ts index 01b6b996aa..3875256bb2 100644 --- a/components/qrcode/interface.ts +++ b/components/qrcode/interface.ts @@ -12,7 +12,9 @@ export const qrProps = () => { return { size: { type: Number, default: 160 }, value: { type: String, required: true }, + type: stringType<'canvas' | 'svg'>('canvas'), color: String, + bgColor: String, includeMargin: Boolean, imageSettings: objectType(), }; From 57d2ae2f009a97ec061b23015edf3b29a730ffb6 Mon Sep 17 00:00:00 2001 From: ZhouWei <1244620067@qq.com> Date: Sun, 25 Jun 2023 22:24:28 +0800 Subject: [PATCH 2/3] fix: responsive loss and invalid border style --- components/qrcode/QRCode.tsx | 110 ++++++++++++------------- components/qrcode/demo/base.vue | 7 +- components/qrcode/demo/customColor.vue | 4 +- components/qrcode/demo/customSize.vue | 2 +- components/qrcode/demo/download.vue | 2 +- components/qrcode/demo/icon.vue | 2 +- components/qrcode/demo/popover.vue | 4 +- components/qrcode/demo/status.vue | 12 +-- components/qrcode/index.tsx | 2 +- 9 files changed, 67 insertions(+), 78 deletions(-) diff --git a/components/qrcode/QRCode.tsx b/components/qrcode/QRCode.tsx index d5c1fa1f76..7e18176c0c 100644 --- a/components/qrcode/QRCode.tsx +++ b/components/qrcode/QRCode.tsx @@ -1,5 +1,5 @@ import type { CSSProperties } from 'vue'; -import { defineComponent, shallowRef, watch, computed, watchEffect, toRefs, unref } from 'vue'; +import { defineComponent, shallowRef, watch, computed, watchEffect } from 'vue'; import type { ImageSettings } from './interface'; import { qrProps } from './interface'; @@ -279,74 +279,68 @@ export const QRCodeSVG = defineComponent({ title: String, }, setup(props) { - const { - value, - size = DEFAULT_SIZE, - level = DEFAULT_LEVEL, - bgColor = DEFAULT_BGCOLOR, - fgColor = DEFAULT_FGCOLOR, - includeMargin = DEFAULT_INCLUDEMARGIN, - title, - marginSize, - imageSettings, - } = toRefs(props); - const _svg = shallowRef(null); - - let cells = qrcodegen.QrCode.encodeText( - unref(value), - ERROR_LEVEL_MAP[unref(level)], - ).getModules(); - - const margin = getMarginSize(unref(includeMargin), unref(marginSize)); - const numCells = cells.length + margin * 2; - const calculatedImageSettings = getImageSettings( - cells, - unref(size), - margin, - unref(imageSettings), - ); + let cells = null; + let margin = null; + let numCells = null; + let calculatedImageSettings = null; + let fgPath = null; let image = null; - if (imageSettings != null && calculatedImageSettings != null) { - if (calculatedImageSettings.excavation != null) { - cells = excavateModules(cells, calculatedImageSettings.excavation); - } - image = ( - - ); - } + watchEffect(() => { + const { + value, + size = DEFAULT_SIZE, + level = DEFAULT_LEVEL, + includeMargin = DEFAULT_INCLUDEMARGIN, + marginSize, + imageSettings, + } = props; + + cells = qrcodegen.QrCode.encodeText(value, ERROR_LEVEL_MAP[level]).getModules(); + + margin = getMarginSize(includeMargin, marginSize); + numCells = cells.length + margin * 2; + calculatedImageSettings = getImageSettings(cells, size, margin, imageSettings); + + if (imageSettings != null && calculatedImageSettings != null) { + if (calculatedImageSettings.excavation != null) { + cells = excavateModules(cells, calculatedImageSettings.excavation); + } - // Drawing strategy: instead of a rect per module, we're going to create a - // single path for the dark modules and layer that on top of a light rect, - // for a total of 2 DOM nodes. We pay a bit more in string concat but that's - // way faster than DOM ops. - // For level 1, 441 nodes -> 2 - // For level 40, 31329 -> 2 - const fgPath = generatePath(cells, margin); + image = ( + + ); + } + + // Drawing strategy: instead of a rect per module, we're going to create a + // single path for the dark modules and layer that on top of a light rect, + // for a total of 2 DOM nodes. We pay a bit more in string concat but that's + // way faster than DOM ops. + // For level 1, 441 nodes -> 2 + // For level 40, 31329 -> 2 + fgPath = generatePath(cells, margin); + }); return () => { + const bgColor = props.bgColor && DEFAULT_BGCOLOR; + const fgColor = props.fgColor && DEFAULT_FGCOLOR; return ( - - {!!title && {title}} + + {!!props.title && {props.title}} - + {image} ); diff --git a/components/qrcode/demo/base.vue b/components/qrcode/demo/base.vue index 20b498af74..6aa48c319b 100644 --- a/components/qrcode/demo/base.vue +++ b/components/qrcode/demo/base.vue @@ -17,12 +17,7 @@ Basic Usage. diff --git a/components/qrcode/demo/customColor.vue b/components/qrcode/demo/customColor.vue index 2ac1a4ed17..8ce1bec3bd 100644 --- a/components/qrcode/demo/customColor.vue +++ b/components/qrcode/demo/customColor.vue @@ -16,9 +16,9 @@ Custom Color. diff --git a/components/qrcode/demo/download.vue b/components/qrcode/demo/download.vue index bb432b3ac6..b82bfd8b8f 100644 --- a/components/qrcode/demo/download.vue +++ b/components/qrcode/demo/download.vue @@ -15,7 +15,7 @@ A way to download QRCode.

Downlaod diff --git a/components/qrcode/demo/icon.vue b/components/qrcode/demo/icon.vue index 76451b54b2..ca53db5f57 100644 --- a/components/qrcode/demo/icon.vue +++ b/components/qrcode/demo/icon.vue @@ -17,7 +17,7 @@ QRCode with Icon. diff --git a/components/qrcode/demo/popover.vue b/components/qrcode/demo/popover.vue index 2f778e317b..7a2cbe160d 100644 --- a/components/qrcode/demo/popover.vue +++ b/components/qrcode/demo/popover.vue @@ -15,9 +15,9 @@ With Popover.