diff --git a/packages/form/widgets/qr-code/demo/simple.md b/packages/form/widgets/qr-code/demo/simple.md
new file mode 100644
index 0000000000..866f3d01de
--- /dev/null
+++ b/packages/form/widgets/qr-code/demo/simple.md
@@ -0,0 +1,67 @@
+---
+title:
+ zh-CN: 基础样例
+ en-US: Basic Usage
+order: 0
+---
+
+## zh-CN
+
+最简单的用法。
+
+## en-US
+
+Simplest of usage.
+
+```ts
+import { Component } from '@angular/core';
+
+import { SFSchema } from '@delon/form';
+import type { SFQrCodeWidgetSchema } from '@delon/form/widgets/qr-code';
+import { NzMessageService } from 'ng-zorro-antd/message';
+
+@Component({
+ selector: 'app-demo',
+ template: ``
+})
+export class DemoComponent {
+ schema: SFSchema = {
+ properties: {
+ base: {
+ type: 'string',
+ title: 'Base',
+ default: 'https://ng-alain.com/',
+ ui: {
+ widget: 'qr-code',
+ refresh: console.log
+ } as SFQrCodeWidgetSchema
+ },
+ icon: {
+ type: 'string',
+ title: 'With Icon',
+ default: 'https://ng-alain.com/',
+ ui: {
+ widget: 'qr-code',
+ icon: 'https://ng-alain.com/assets/logo-color.svg',
+ bordered: true
+ } as SFQrCodeWidgetSchema
+ },
+ color: {
+ type: 'string',
+ title: 'Color',
+ default: 'https://ng-alain.com/',
+ ui: {
+ widget: 'qr-code',
+ color: '#f50'
+ } as SFQrCodeWidgetSchema
+ }
+ }
+ };
+
+ constructor(private msg: NzMessageService) {}
+
+ submit(value: {}): void {
+ this.msg.success(JSON.stringify(value));
+ }
+}
+```
diff --git a/packages/form/widgets/qr-code/index.en-US.md b/packages/form/widgets/qr-code/index.en-US.md
new file mode 100644
index 0000000000..83e40a3b5a
--- /dev/null
+++ b/packages/form/widgets/qr-code/index.en-US.md
@@ -0,0 +1,28 @@
+---
+title: qr-code
+subtitle: QRCode
+type: Non-built-in widgets
+---
+
+Used when the link needs to be converted into a QR Code.
+
+## Import module
+
+Non-built-in modules, Should be import `QrCodeWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11).
+
+## API
+
+### ui
+
+| Property | Description | Type | Default |
+|----------|-------------|------|---------|
+| `[color]` | QR code Color | `string` | `#000` |
+| `[bgColor]` | QR code background color | `string` | `#FFFFFF` |
+| `[qrSize]` | QR code Size | `number` | `160` |
+| `[padding]` | QR code Padding | `number \| number[]` | `0` |
+| `[icon]` | Icon address in QR code | `string` | - |
+| `[iconSize]` | The size of the icon in the QR code | `number` | `40` |
+| `[bordered]` | Whether has border style | `boolean` | `true` |
+| `[status]` | QR code status | `'active'|'expired' |'loading'` | `active` |
+| `[level]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `M` |
+| `(refresh)` | callback | `EventEmitter` | - |
diff --git a/packages/form/widgets/qr-code/index.ts b/packages/form/widgets/qr-code/index.ts
new file mode 100644
index 0000000000..e97ca0fae5
--- /dev/null
+++ b/packages/form/widgets/qr-code/index.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+
+import { DelonFormModule, WidgetRegistry } from '@delon/form';
+import { NzQRCodeModule } from 'ng-zorro-antd/qr-code';
+
+import { QrCodeWidget } from './widget';
+
+export * from './widget';
+export * from './schema';
+
+@NgModule({
+ imports: [FormsModule, DelonFormModule, NzQRCodeModule],
+ declarations: [QrCodeWidget]
+})
+export class QrCodeWidgetModule {
+ constructor(widgetRegistry: WidgetRegistry) {
+ widgetRegistry.register(QrCodeWidget.KEY, QrCodeWidget);
+ }
+}
diff --git a/packages/form/widgets/qr-code/index.zh-CN.md b/packages/form/widgets/qr-code/index.zh-CN.md
new file mode 100644
index 0000000000..32076a1edc
--- /dev/null
+++ b/packages/form/widgets/qr-code/index.zh-CN.md
@@ -0,0 +1,28 @@
+---
+title: qr-code
+subtitle: 二维码
+type: Non-built-in widgets
+---
+
+当需要将链接转换成为二维码时使用。
+
+## 导入模块
+
+非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `QrCodeWidgetModule`。
+
+## API
+
+### ui 属性
+
+| 成员 | 说明 | 类型 | 默认值 |
+|----|----|----|-----|
+| `[color]` | 二维码颜色 | `string` | `#000` |
+| `[bgColor]` | 二维码背景颜色 | `string` | `#FFFFFF` |
+| `[qrSize]` | 二维码大小 | `number` | `160` |
+| `[padding]` | 二维码填充 | `number \| number[]` | `0` |
+| `[icon]` | 二维码中 icon 地址 | `string` | - |
+| `[iconSize]` | 二维码中 icon 大小 | `number` | `40` |
+| `[bordered]` | 是否有边框 | `boolean` | `true` |
+| `[status]` | 二维码状态 | `'active'|'expired' |'loading'` | `active` |
+| `[level]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `M` |
+| `(refresh)` | 点击"点击刷新"的回调 | `EventEmitter` | - |
diff --git a/packages/form/widgets/qr-code/ng-package.json b/packages/form/widgets/qr-code/ng-package.json
new file mode 100644
index 0000000000..3b78722325
--- /dev/null
+++ b/packages/form/widgets/qr-code/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "lib": {
+ "flatModuleFile": "widgets-qr-code",
+ "entryFile": "index.ts"
+ }
+}
diff --git a/packages/form/widgets/qr-code/schema.ts b/packages/form/widgets/qr-code/schema.ts
new file mode 100644
index 0000000000..49be6274ba
--- /dev/null
+++ b/packages/form/widgets/qr-code/schema.ts
@@ -0,0 +1,25 @@
+import type { SFUISchemaItem } from '@delon/form';
+import type { ERROR_LEVEL_MAP } from 'ng-zorro-antd/qr-code/qrcode';
+
+export interface SFQrCodeWidgetSchema extends SFUISchemaItem {
+ /** QR code Padding */
+ padding?: number;
+ /** QR code Color */
+ color?: string;
+ /** QR code background color */
+ bgColor?: string;
+ /** QR code Size */
+ qrSize?: number;
+ /** Icon address in QR code */
+ icon?: string;
+ /** The size of the icon in the QR code */
+ iconSize?: number;
+ /** Whether has border style */
+ bordered?: boolean;
+ /** QR code status */
+ status?: 'active' | 'expired' | 'loading';
+ /** Error Code Level */
+ level?: keyof typeof ERROR_LEVEL_MAP;
+ /** Callback */
+ refresh?: (qr: string) => void;
+}
diff --git a/packages/form/widgets/qr-code/widget.spec.ts b/packages/form/widgets/qr-code/widget.spec.ts
new file mode 100644
index 0000000000..6f348610a9
--- /dev/null
+++ b/packages/form/widgets/qr-code/widget.spec.ts
@@ -0,0 +1,37 @@
+import { DebugElement } from '@angular/core';
+import { ComponentFixture, fakeAsync } from '@angular/core/testing';
+
+import { SFSchema } from '@delon/form';
+import { createTestContext } from '@delon/testing';
+
+import { QrCodeWidgetModule, SFQrCodeWidgetSchema } from './index';
+import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec';
+
+describe('form: widget: qr-code', () => {
+ let fixture: ComponentFixture;
+ let dl: DebugElement;
+ let context: TestFormComponent;
+ let page: SFPage;
+
+ configureSFTestSuite({ imports: [QrCodeWidgetModule] });
+
+ beforeEach(() => {
+ ({ fixture, dl, context } = createTestContext(TestFormComponent));
+ page = new SFPage(context.comp);
+ page.cleanOverlay().prop(dl, context, fixture);
+ });
+
+ it('should be working', fakeAsync(() => {
+ const s: SFSchema = {
+ properties: {
+ a: {
+ type: 'string',
+ ui: {
+ widget: 'qr-code'
+ } as SFQrCodeWidgetSchema
+ }
+ }
+ };
+ page.newSchema(s).getEl('canvas');
+ }));
+});
diff --git a/packages/form/widgets/qr-code/widget.ts b/packages/form/widgets/qr-code/widget.ts
new file mode 100644
index 0000000000..db3c94cb96
--- /dev/null
+++ b/packages/form/widgets/qr-code/widget.ts
@@ -0,0 +1,41 @@
+import { Component, ViewEncapsulation } from '@angular/core';
+
+import { ControlUIWidget } from '@delon/form';
+
+import type { SFQrCodeWidgetSchema } from './schema';
+
+@Component({
+ selector: 'sf-qr-code',
+ template: `
+
+ `,
+ preserveWhitespaces: false,
+ encapsulation: ViewEncapsulation.None
+})
+export class QrCodeWidget extends ControlUIWidget {
+ static readonly KEY = 'qr-code';
+
+ refresh(qr: string): void {
+ this.setValue(qr);
+ if (this.ui.refresh) this.ui.refresh(qr);
+ }
+}
diff --git a/scripts/site/route-paths.txt b/scripts/site/route-paths.txt
index 2d5bc2f1f1..cf57abb3e2 100644
--- a/scripts/site/route-paths.txt
+++ b/scripts/site/route-paths.txt
@@ -233,6 +233,8 @@
/form/object/zh
/form/qa/en
/form/qa/zh
+/form/qr-code/en
+/form/qr-code/zh
/form/radio/en
/form/radio/zh
/form/rate/en
diff --git a/src/app/shared/json-schema/json-schema.module.ts b/src/app/shared/json-schema/json-schema.module.ts
index 4c615b7b3a..61bb9e095b 100644
--- a/src/app/shared/json-schema/json-schema.module.ts
+++ b/src/app/shared/json-schema/json-schema.module.ts
@@ -5,6 +5,7 @@ import { AutoCompleteWidgetModule } from '@delon/form/widgets/autocomplete';
import { CascaderWidgetModule } from '@delon/form/widgets/cascader';
import { ColorWidgetModule } from '@delon/form/widgets/color';
import { MentionWidgetModule } from '@delon/form/widgets/mention';
+import { QrCodeWidgetModule } from '@delon/form/widgets/qr-code';
import { RateWidgetModule } from '@delon/form/widgets/rate';
import { SliderWidgetModule } from '@delon/form/widgets/slider';
import { TagWidgetModule } from '@delon/form/widgets/tag';
@@ -32,6 +33,7 @@ import { SharedModule } from '../shared.module';
TimeWidgetModule,
UploadWidgetModule,
ColorWidgetModule,
+ QrCodeWidgetModule,
MonacoEditorWidgetModule,
TinymceWidgetModule
]