mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-04-18 14:50:56 +08:00
feat(n-qrcode): add n-qrcode
component (#4963)
* feat(n-qrcode): add `n-qrcode` component * fix(n-qrcode): value desc when enUS * fix(n-qrcode): docs error when enUS * feat(n-qrcode): add n-padding * fix: remove type as * feat: switch watch to watchEffect * feat(n-qrcode): add `color` `bg-color` props * refactor: change to QR Code generator library * feat: add size demo * chore: remove border * feat: add error-correction-level * feat: add docs * feat: add icon * feat: add download demo * fix: image scale * fix: docs error * fix: docs error --------- Co-authored-by: lijiaheng <lijiaheng@semi-tech.com> Co-authored-by: 07akioni <07akioni2@gmail.com>
This commit is contained in:
parent
ea2603ed4b
commit
cdc48c7a7d
.eslintrc.jsCHANGELOG.en-US.mdCHANGELOG.zh-CN.md
demo
src
components.tsindex.ts
config-provider/src
qrcode
demos
enUS
basic.demo.vueborder.demo.vuecolor.demo.vuedownload.demo.vueerror-correction.demo.vueicon.demo.vueindex.demo-entry.mdsize.demo.vue
zhCN
src
styles
themes
@ -47,6 +47,7 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-misused-promises': 0,
|
||||
'@typescript-eslint/strict-boolean-expressions': 0,
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 0,
|
||||
'@typescript-eslint/naming-convention': 0,
|
||||
@ -88,6 +89,14 @@ module.exports = {
|
||||
'@typescript-eslint/no-floating-promises': 0
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['qrcodegen.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-namespace': 0,
|
||||
eqeqeq: 0,
|
||||
'no-useless-escape': 0
|
||||
}
|
||||
},
|
||||
{
|
||||
files: '*',
|
||||
globals: {
|
||||
|
@ -37,6 +37,7 @@
|
||||
- `n-popselect` adds `header` slot.
|
||||
- `n-tree-select` adds `watch-props` prop.
|
||||
- Adds `n-split` component, closes [#3557](https://github.com/tusen-ai/naive-ui/issues/3557).
|
||||
- adds `n-qrcode` component, closes [#2535](https://github.com/tusen-ai/naive-ui/issues/2535)
|
||||
|
||||
|
||||
## 2.35.0
|
||||
|
@ -37,6 +37,7 @@
|
||||
- `n-popselect` 新增 `header` 插槽
|
||||
- `n-tree-select` 新增 `watch-props` 属性
|
||||
- 新增 `n-split` 组件,关闭 [#3557](https://github.com/tusen-ai/naive-ui/issues/3557)
|
||||
- 新增 `n-qrcode` 组件,关闭 [#2535](https://github.com/tusen-ai/naive-ui/issues/2535)
|
||||
|
||||
## 2.35.0
|
||||
|
||||
|
@ -535,6 +535,10 @@ export const enComponentRoutes = [
|
||||
path: 'equation',
|
||||
component: () => import('../../src/equation/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
component: () => import('../../src/qrcode/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'virtual-list',
|
||||
component: () =>
|
||||
@ -921,6 +925,10 @@ export const zhComponentRoutes = [
|
||||
path: 'equation',
|
||||
component: () => import('../../src/equation/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
component: () => import('../../src/qrcode/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'virtual-list',
|
||||
component: () =>
|
||||
|
@ -461,6 +461,12 @@ export function createComponentMenuOptions ({ lang, theme, mode }) {
|
||||
enSuffix: true,
|
||||
path: '/number-animation'
|
||||
},
|
||||
{
|
||||
en: 'QRCode',
|
||||
zh: '二维码',
|
||||
enSuffix: true,
|
||||
path: '/qrcode'
|
||||
},
|
||||
{
|
||||
en: 'Statistic',
|
||||
zh: '统计数据',
|
||||
|
@ -59,6 +59,7 @@ export * from './popconfirm'
|
||||
export * from './popover'
|
||||
export * from './popselect'
|
||||
export * from './progress'
|
||||
export * from './qrcode'
|
||||
export * from './radio'
|
||||
export * from './rate'
|
||||
export * from './result'
|
||||
|
@ -53,6 +53,7 @@ import type { PopconfirmTheme } from '../../popconfirm/styles'
|
||||
import type { PopoverTheme } from '../../popover/styles'
|
||||
import type { PopselectTheme } from '../../popselect/styles'
|
||||
import type { ProgressTheme } from '../../progress/styles'
|
||||
import type { QrcodeTheme } from '../../qrcode/styles'
|
||||
import type { RadioTheme } from '../../radio/styles'
|
||||
import type { RateTheme } from '../../rate/styles'
|
||||
import type { ResultTheme } from '../../result/styles'
|
||||
@ -155,6 +156,7 @@ export interface GlobalThemeWithoutCommon {
|
||||
Popover?: PopoverTheme
|
||||
Popselect?: PopselectTheme
|
||||
Progress?: ProgressTheme
|
||||
Qrcode?: QrcodeTheme
|
||||
Radio?: RadioTheme
|
||||
Rate?: RateTheme
|
||||
Result?: ResultTheme
|
||||
|
25
src/qrcode/demos/enUS/basic.demo.vue
Normal file
25
src/qrcode/demos/enUS/basic.demo.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<markdown>
|
||||
# Basic
|
||||
|
||||
A basic Qrcode.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-qrcode :value="text" />
|
||||
<n-input v-model:value="text" :maxlength="60" type="text" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const text = ref('The rain dampened the sky')
|
||||
return {
|
||||
text
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
9
src/qrcode/demos/enUS/border.demo.vue
Normal file
9
src/qrcode/demos/enUS/border.demo.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<markdown>
|
||||
# Border
|
||||
|
||||
Qrcode can be used without border.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-qrcode :bordered="false" value="https://www.naiveui.com/" />
|
||||
</template>
|
16
src/qrcode/demos/enUS/color.demo.vue
Normal file
16
src/qrcode/demos/enUS/color.demo.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<markdown>
|
||||
# Color
|
||||
|
||||
Let the QR code no longer monotonous.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space>
|
||||
<n-qrcode value="https://www.naiveui.com/" color="#18a058" />
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
color="#409eff"
|
||||
background-color="#F5F5F5"
|
||||
/>
|
||||
</n-space>
|
||||
</template>
|
41
src/qrcode/demos/enUS/download.demo.vue
Normal file
41
src/qrcode/demos/enUS/download.demo.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<markdown>
|
||||
# Download
|
||||
|
||||
Download two-dimensional code code implementation, you can also choose to right-click the picture save as.
|
||||
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-qrcode id="qrcode" value="https://www.naiveui.com/" />
|
||||
<n-button @click="handleDownloadQRCode">
|
||||
Download
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const handleDownloadQRCode = () => {
|
||||
const canvas = document
|
||||
.querySelector('#qrcode')
|
||||
?.querySelector<HTMLCanvasElement>('canvas')
|
||||
if (canvas) {
|
||||
const url = canvas.toDataURL()
|
||||
const a = document.createElement('a')
|
||||
a.download = 'QRCode.png'
|
||||
a.href = url
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
}
|
||||
}
|
||||
return {
|
||||
handleDownloadQRCode
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
42
src/qrcode/demos/enUS/error-correction.demo.vue
Normal file
42
src/qrcode/demos/enUS/error-correction.demo.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<markdown>
|
||||
# Error correction
|
||||
|
||||
Use `error-correction-level` to set the error correction level.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-qrcode
|
||||
value="Like a humorous magician, he skillfully turns tedious information into a mysterious QR code"
|
||||
:error-correction-level="errorCorrectionLevel"
|
||||
/>
|
||||
<n-radio-group v-model:value="errorCorrectionLevel">
|
||||
<n-radio-button
|
||||
v-for="errorCorrection in errorCorrectionOptions"
|
||||
:key="errorCorrection.value"
|
||||
:value="errorCorrection.value"
|
||||
:label="errorCorrection.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const errorCorrectionLevel = ref('M')
|
||||
const errorCorrectionOptions = [
|
||||
{ value: 'L', label: 'L' },
|
||||
{ value: 'M', label: 'M' },
|
||||
{ value: 'Q', label: 'Q' },
|
||||
{ value: 'H', label: 'H' }
|
||||
]
|
||||
return {
|
||||
errorCorrectionLevel,
|
||||
errorCorrectionOptions
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
37
src/qrcode/demos/enUS/icon.demo.vue
Normal file
37
src/qrcode/demos/enUS/icon.demo.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<markdown>
|
||||
# Icon
|
||||
|
||||
You can put some representative Icon.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space>
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
icon="https://www.naiveui.com/assets/naivelogo-93278402.svg"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
icon="https://www.naiveui.com/assets/naivelogo-93278402.svg"
|
||||
icon-background-color="#ccc"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
icon="https://www.naiveui.com/assets/naivelogo-93278402.svg"
|
||||
:icon-size="48"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
</script>
|
49
src/qrcode/demos/enUS/index.demo-entry.md
Normal file
49
src/qrcode/demos/enUS/index.demo-entry.md
Normal file
@ -0,0 +1,49 @@
|
||||
# QRCode
|
||||
|
||||
Like a humorous magician, he skillfully turns tedious information into a mysterious QR code
|
||||
|
||||
## 演示
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
icon.vue
|
||||
border.vue
|
||||
size.vue
|
||||
color.vue
|
||||
error-correction.vue
|
||||
download.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### QRCode Props
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| background-color | `string` | `#FFF` | Qr code background color, Values need to be in `hex` format. | NEXT |
|
||||
| bordered | `boolean` | `true` | Whether to show the qrcode border. | NEXT |
|
||||
| color | `string` | `#000` | Qr code color, Values need to be in `hex` format. | NEXT |
|
||||
| error-correction-level | `L` \| `M` \| `Q` \| `H` | `M` | Qr code error correction level. | NEXT |
|
||||
| icon | `string` | `undefined` | Icon's URL. | NEXT |
|
||||
| icon-size | `number` | `40` | Icon's size. | NEXT |
|
||||
| icon-background-color | `string` | `#FFF` | Icon's background color. | NEXT |
|
||||
| value | `string` | `-` | Text information. | NEXT |
|
||||
| size | `number` | `160` | Size of the qrcode. | NEXT |
|
||||
|
||||
## Q & A
|
||||
|
||||
### About QR code error correction level
|
||||
|
||||
The error correction level of the two-dimensional code refers to the error correction capability used in the generation of the two-dimensional code, which determines the ability of the two-dimensional code to be correctly decoded when it is damaged or partially invisible.
|
||||
|
||||
Two-dimensional code standards (such as QR codes) define four error correction levels: L, M, Q, and H. Each level provides different error correction capabilities and tolerance.
|
||||
|
||||
- 'L (Low)' : :Provides approximately `7%` recovery capability
|
||||
|
||||
- `M (Middle)`:Provides approximately `15%` recovery capacity
|
||||
|
||||
- `Q (High)`:Provides approximately `25%` recovery capacity
|
||||
|
||||
- `H (Max)`:Provides approximately `30%` recovery capacity
|
||||
|
||||
Selecting a higher error correction level can improve the fault tolerance of the two-dimensional code, that is, it can still be correctly decoded under a certain degree of damage or deformation, but at the same time, it will increase the density of the two-dimensional code, making the two-dimensional code occupy a larger space.
|
52
src/qrcode/demos/enUS/size.demo.vue
Normal file
52
src/qrcode/demos/enUS/size.demo.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<markdown>
|
||||
# Size
|
||||
|
||||
It is like a crazy puff pastry, no matter how big or small, it will lead you into another mysterious information space.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-space>
|
||||
<n-button @click="minus">
|
||||
Minus 10
|
||||
</n-button>
|
||||
<n-button @click="add">
|
||||
Add 10
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-qrcode :value="text" :size="size" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const text = ref('The rain dampened the sky')
|
||||
|
||||
const size = ref(110)
|
||||
|
||||
const add = () => {
|
||||
size.value += 10
|
||||
if (size.value > 200) {
|
||||
size.value = 110
|
||||
}
|
||||
}
|
||||
|
||||
const minus = () => {
|
||||
size.value -= 10
|
||||
if (size.value < 20) {
|
||||
size.value = 110
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
size,
|
||||
text,
|
||||
add,
|
||||
minus
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
25
src/qrcode/demos/zhCN/basic.demo.vue
Normal file
25
src/qrcode/demos/zhCN/basic.demo.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<markdown>
|
||||
# 基础用法
|
||||
|
||||
基础二维码。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-qrcode :value="text" />
|
||||
<n-input v-model:value="text" :maxlength="60" type="text" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const text = ref('雨淋湿了天空')
|
||||
return {
|
||||
text
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
9
src/qrcode/demos/zhCN/border.demo.vue
Normal file
9
src/qrcode/demos/zhCN/border.demo.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<markdown>
|
||||
# 边框
|
||||
|
||||
二维码可以没有边框。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-qrcode :bordered="false" value="https://www.naiveui.com/" />
|
||||
</template>
|
16
src/qrcode/demos/zhCN/color.demo.vue
Normal file
16
src/qrcode/demos/zhCN/color.demo.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<markdown>
|
||||
# 颜色
|
||||
|
||||
让二维码不再单调乏味。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space>
|
||||
<n-qrcode value="https://www.naiveui.com/" color="#18a058" />
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
color="#409eff"
|
||||
background-color="#F5F5F5"
|
||||
/>
|
||||
</n-space>
|
||||
</template>
|
40
src/qrcode/demos/zhCN/download.demo.vue
Normal file
40
src/qrcode/demos/zhCN/download.demo.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<markdown>
|
||||
# 下载
|
||||
|
||||
下载二维码的代码实现,你也可以选择右键图片另存为
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-qrcode id="qrcode" value="https://www.naiveui.com/" />
|
||||
<n-button @click="handleDownloadQRCode">
|
||||
下载
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const handleDownloadQRCode = () => {
|
||||
const canvas = document
|
||||
.querySelector('#qrcode')
|
||||
?.querySelector<HTMLCanvasElement>('canvas')
|
||||
if (canvas) {
|
||||
const url = canvas.toDataURL()
|
||||
const a = document.createElement('a')
|
||||
a.download = 'QRCode.png'
|
||||
a.href = url
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
}
|
||||
}
|
||||
return {
|
||||
handleDownloadQRCode
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
42
src/qrcode/demos/zhCN/error-correction.demo.vue
Normal file
42
src/qrcode/demos/zhCN/error-correction.demo.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<markdown>
|
||||
# 纠错
|
||||
|
||||
使用 `error-correction-level` 来设定纠错级别。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-qrcode
|
||||
value="犹如一位幽默风趣的魔术师,巧妙地将繁琐的信息变成了一个神秘的二维码"
|
||||
:error-correction-level="errorCorrectionLevel"
|
||||
/>
|
||||
<n-radio-group v-model:value="errorCorrectionLevel">
|
||||
<n-radio-button
|
||||
v-for="errorCorrection in errorCorrectionOptions"
|
||||
:key="errorCorrection.value"
|
||||
:value="errorCorrection.value"
|
||||
:label="errorCorrection.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const errorCorrectionLevel = ref('M')
|
||||
const errorCorrectionOptions = [
|
||||
{ value: 'L', label: 'L' },
|
||||
{ value: 'M', label: 'M' },
|
||||
{ value: 'Q', label: 'Q' },
|
||||
{ value: 'H', label: 'H' }
|
||||
]
|
||||
return {
|
||||
errorCorrectionLevel,
|
||||
errorCorrectionOptions
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
37
src/qrcode/demos/zhCN/icon.demo.vue
Normal file
37
src/qrcode/demos/zhCN/icon.demo.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<markdown>
|
||||
# 图标
|
||||
|
||||
可以放一些代表性的 Icon
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space>
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
icon="https://www.naiveui.com/assets/naivelogo-93278402.svg"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
icon="https://www.naiveui.com/assets/naivelogo-93278402.svg"
|
||||
icon-background-color="#ccc"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
<n-qrcode
|
||||
value="https://www.naiveui.com/"
|
||||
icon="https://www.naiveui.com/assets/naivelogo-93278402.svg"
|
||||
:icon-size="48"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
</script>
|
49
src/qrcode/demos/zhCN/index.demo-entry.md
Normal file
49
src/qrcode/demos/zhCN/index.demo-entry.md
Normal file
@ -0,0 +1,49 @@
|
||||
# 二维码 QRCode
|
||||
|
||||
犹如一位幽默风趣的魔术师,巧妙地将繁琐的信息变成了一个神秘的二维码
|
||||
|
||||
## 演示
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
icon.vue
|
||||
border.vue
|
||||
size.vue
|
||||
color.vue
|
||||
error-correction.vue
|
||||
download.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### QRCode Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| background-color | `string` | `#FFF` | 二维码背景颜色,值需要采用 `hex` 格式 | NEXT |
|
||||
| bordered | `boolean` | `true` | 是否显示二维码边框 | NEXT |
|
||||
| color | `string` | `#000` | 二维码颜色,值需要采用 `hex` 格式 | NEXT |
|
||||
| error-correction-level | `L` \| `M` \| `Q` \| `H` | `M` | 二维码纠错级别 | NEXT |
|
||||
| icon | `string` | `undefined` | 图标地址 | NEXT |
|
||||
| icon-size | `number` | `40` | 图标大小 | NEXT |
|
||||
| icon-background-color | `string` | `#FFF` | 图标背景颜色 | NEXT |
|
||||
| value | `string` | `-` | 文本信息 | NEXT |
|
||||
| size | `number` | `160` | 二维码大小 | NEXT |
|
||||
|
||||
## Q & A
|
||||
|
||||
### 关于二维码纠错级别
|
||||
|
||||
二维码纠错级别是指在生成二维码时所使用的错误纠正能力,它决定了二维码在受到损坏或部分不可见时,仍然可以正确解码的能力。
|
||||
|
||||
二维码标准(如 QR 码)定义了四个纠错级别:L、M、Q 和 H。每个级别提供不同的纠错能力和容错性。
|
||||
|
||||
- `L(低)`:提供约 `7%` 的恢复能力
|
||||
|
||||
- `M(中)`:提供约 `15%` 的恢复能力
|
||||
|
||||
- `Q(高)`:提供约 `25%` 的恢复能力
|
||||
|
||||
- `H(最高)`:提供约 `30%` 的恢复能力
|
||||
|
||||
选择更高的纠错级别可以提高二维码的容错性,即在一定程度的损坏或变形下仍然能够正确解码,但同时会增加二维码的密度,使得二维码所占空间更大。
|
52
src/qrcode/demos/zhCN/size.demo.vue
Normal file
52
src/qrcode/demos/zhCN/size.demo.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<markdown>
|
||||
# 尺寸
|
||||
|
||||
它就像疯狂千层饼,无论大小,都会引领你进入另一个神秘的信息空间。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-space>
|
||||
<n-button @click="minus">
|
||||
减 10
|
||||
</n-button>
|
||||
<n-button @click="add">
|
||||
加 10
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-qrcode :value="text" :size="size" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const text = ref('The rain dampened the sky')
|
||||
|
||||
const size = ref(110)
|
||||
|
||||
const add = () => {
|
||||
size.value += 10
|
||||
if (size.value > 200) {
|
||||
size.value = 110
|
||||
}
|
||||
}
|
||||
|
||||
const minus = () => {
|
||||
size.value -= 10
|
||||
if (size.value < 20) {
|
||||
size.value = 110
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
size,
|
||||
text,
|
||||
add,
|
||||
minus
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
2
src/qrcode/index.ts
Normal file
2
src/qrcode/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as NQrcode, qrcodeProps } from './src/Qrcode'
|
||||
export type { QrcodeProps } from './src/Qrcode'
|
141
src/qrcode/src/Qrcode.tsx
Normal file
141
src/qrcode/src/Qrcode.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
import { h, ref, defineComponent, watchEffect } from 'vue'
|
||||
import { useConfig, useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import type { ExtractPublicPropTypes } from '../../_utils'
|
||||
import style from './styles/index.cssr'
|
||||
import { type QrcodeTheme, qrcodeLight } from '../styles'
|
||||
import qrcodegen from './qrcodegen'
|
||||
|
||||
const ERROR_CORRECTION_LEVEL: Record<string, qrcodegen.QrCode.Ecc> = {
|
||||
L: qrcodegen.QrCode.Ecc.LOW,
|
||||
M: qrcodegen.QrCode.Ecc.MEDIUM,
|
||||
Q: qrcodegen.QrCode.Ecc.QUARTILE,
|
||||
H: qrcodegen.QrCode.Ecc.HIGH
|
||||
}
|
||||
|
||||
export const qrcodeProps = {
|
||||
...(useTheme.props as ThemeProps<QrcodeTheme>),
|
||||
value: String,
|
||||
color: {
|
||||
type: String,
|
||||
default: '#000'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: '#FFF'
|
||||
},
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
icon: String,
|
||||
iconSize: {
|
||||
type: Number,
|
||||
default: 40
|
||||
},
|
||||
iconBackgroundColor: {
|
||||
type: String,
|
||||
default: '#FFF'
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 110
|
||||
},
|
||||
errorCorrectionLevel: {
|
||||
type: String,
|
||||
default: 'M'
|
||||
}
|
||||
} as const
|
||||
|
||||
export type QrcodeProps = ExtractPublicPropTypes<typeof qrcodeProps>
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Qrcode',
|
||||
props: qrcodeProps,
|
||||
setup (props) {
|
||||
const { mergedClsPrefixRef } = useConfig(props)
|
||||
|
||||
useTheme('Qrcode', '-qrcode', style, qrcodeLight, props, mergedClsPrefixRef)
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement>()
|
||||
|
||||
watchEffect(() => {
|
||||
const errorCorrectionLevel =
|
||||
ERROR_CORRECTION_LEVEL[props.errorCorrectionLevel]
|
||||
const qr = qrcodegen.QrCode.encodeText(
|
||||
props.value ?? '-',
|
||||
errorCorrectionLevel
|
||||
)
|
||||
drawCanvas(qr, props.size, props.color, props.backgroundColor)
|
||||
})
|
||||
|
||||
function drawCanvas (
|
||||
qr: qrcodegen.QrCode,
|
||||
size: number,
|
||||
darkColor: string,
|
||||
lightColor: string
|
||||
): void {
|
||||
const canvas = canvasRef.value
|
||||
if (!canvas) return
|
||||
const canvasWidth = size
|
||||
const width = qr.size
|
||||
const scale = canvasWidth / width
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = canvasWidth
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
for (let y = 0; y < qr.size; y++) {
|
||||
for (let x = 0; x < qr.size; x++) {
|
||||
ctx.fillStyle = qr.getModule(x, y) ? darkColor : lightColor
|
||||
const startX = Math.floor(x * scale)
|
||||
const endX = Math.ceil((x + 1) * scale)
|
||||
const startY = Math.floor(y * scale)
|
||||
const endY = Math.ceil((y + 1) * scale)
|
||||
ctx.fillRect(startX, startY, endX - startX, endY - startY)
|
||||
}
|
||||
}
|
||||
if (props.icon) {
|
||||
const img = new Image()
|
||||
img.src = props.icon
|
||||
img.onload = () => {
|
||||
const iconSize = props.iconSize
|
||||
const centerX = (canvas.width - iconSize) / 2
|
||||
const centerY = (canvas.height - iconSize) / 2
|
||||
ctx.fillStyle = props.iconBackgroundColor
|
||||
ctx.fillRect(centerX, centerY, iconSize, iconSize)
|
||||
const aspectRatio = img.width / img.height
|
||||
const scaledWidth =
|
||||
aspectRatio >= 1 ? iconSize : iconSize * aspectRatio
|
||||
const scaledHeight =
|
||||
aspectRatio <= 1 ? iconSize : iconSize / aspectRatio
|
||||
const left = centerX + (iconSize - scaledWidth) / 2
|
||||
const top = centerY + (iconSize - scaledHeight) / 2
|
||||
ctx.drawImage(img, left, top, scaledWidth, scaledHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
canvasRef,
|
||||
mergedClsPrefix: mergedClsPrefixRef
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { mergedClsPrefix, bordered, backgroundColor } = this
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
`${mergedClsPrefix}-qrcode`,
|
||||
{ [`${mergedClsPrefix}-qrcode--bordered`]: bordered }
|
||||
]}
|
||||
>
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-qrcode-wrapper`]}
|
||||
style={{ backgroundColor }}
|
||||
>
|
||||
<canvas ref="canvasRef"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
1130
src/qrcode/src/qrcodegen.ts
Normal file
1130
src/qrcode/src/qrcodegen.ts
Normal file
File diff suppressed because it is too large
Load Diff
30
src/qrcode/src/styles/index.cssr.ts
Normal file
30
src/qrcode/src/styles/index.cssr.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { c, cB, cM } from '../../../_utils/cssr'
|
||||
|
||||
// vars:
|
||||
// --n-border-color
|
||||
// --n-border-radius
|
||||
|
||||
export default c([
|
||||
cB('qrcode', [
|
||||
cB('qrcode-wrapper', `
|
||||
width: fit-content;
|
||||
line-height: 0;
|
||||
`),
|
||||
cM('bordered', [
|
||||
c('>',
|
||||
[
|
||||
cB('qrcode-wrapper', `
|
||||
border: 1px solid var(--n-border-color);
|
||||
border-radius: var(--n-border-radius) var(--n-border-radius) 0 0;
|
||||
padding:
|
||||
var(--n-padding-top)
|
||||
var(--n-padding-left)
|
||||
var(--n-padding-bottom)
|
||||
var(--n-padding-left);
|
||||
`)
|
||||
]
|
||||
)
|
||||
])
|
||||
]
|
||||
)
|
||||
])
|
9
src/qrcode/styles/dark.ts
Normal file
9
src/qrcode/styles/dark.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { commonDark } from '../../_styles/common'
|
||||
import { type QrcodeTheme } from './light'
|
||||
|
||||
const qrcodeDark: QrcodeTheme = {
|
||||
name: 'Qrcode',
|
||||
common: commonDark
|
||||
}
|
||||
|
||||
export default qrcodeDark
|
3
src/qrcode/styles/index.ts
Normal file
3
src/qrcode/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as qrcodeDark } from './dark'
|
||||
export { default as qrcodeLight } from './light'
|
||||
export type { QrcodeTheme, QrcodeThemeVars } from './light'
|
18
src/qrcode/styles/light.ts
Normal file
18
src/qrcode/styles/light.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { commonLight } from '../../_styles/common'
|
||||
import type { ThemeCommonVars } from '../../_styles/common'
|
||||
import { type Theme } from '../../_mixins'
|
||||
|
||||
const self = (vars: ThemeCommonVars) => {
|
||||
return {}
|
||||
}
|
||||
|
||||
export type QrcodeThemeVars = ReturnType<typeof self>
|
||||
|
||||
const themeLight: Theme<'Qrcode', QrcodeThemeVars> = {
|
||||
name: 'Qrcode',
|
||||
common: commonLight,
|
||||
self
|
||||
}
|
||||
|
||||
export default themeLight
|
||||
export type QrcodeTheme = typeof themeLight
|
@ -54,6 +54,7 @@ import { popconfirmDark } from '../popconfirm/styles'
|
||||
import { popoverDark } from '../popover/styles'
|
||||
import { popselectDark } from '../popselect/styles'
|
||||
import { progressDark } from '../progress/styles'
|
||||
import { qrcodeDark } from '../qrcode/styles'
|
||||
import { radioDark } from '../radio/styles'
|
||||
import { rateDark } from '../rate/styles'
|
||||
import { resultDark } from '../result/styles'
|
||||
@ -141,6 +142,7 @@ export const darkTheme: BuiltInGlobalTheme = {
|
||||
Popover: popoverDark,
|
||||
Popselect: popselectDark,
|
||||
Progress: progressDark,
|
||||
Qrcode: qrcodeDark,
|
||||
Radio: radioDark,
|
||||
Rate: rateDark,
|
||||
Result: resultDark,
|
||||
|
@ -56,6 +56,7 @@ import { popconfirmLight } from '../popconfirm/styles'
|
||||
import { popoverLight } from '../popover/styles'
|
||||
import { popselectLight } from '../popselect/styles'
|
||||
import { progressLight } from '../progress/styles'
|
||||
import { qrcodeLight } from '../qrcode/styles'
|
||||
import { radioLight } from '../radio/styles'
|
||||
import { rateLight } from '../rate/styles'
|
||||
import { resultLight } from '../result/styles'
|
||||
@ -143,6 +144,7 @@ export const lightTheme: BuiltInGlobalTheme = {
|
||||
Popover: popoverLight,
|
||||
Popselect: popselectLight,
|
||||
Progress: progressLight,
|
||||
Qrcode: qrcodeLight,
|
||||
Radio: radioLight,
|
||||
Rate: rateLight,
|
||||
Row: rowLight,
|
||||
|
Loading…
x
Reference in New Issue
Block a user