Skip to content

Commit

Permalink
Merge pull request baidu#9726 from allenve/master
Browse files Browse the repository at this point in the history
feat:amis支持手写签名面板
  • Loading branch information
hsm-lv committed Mar 5, 2024
2 parents 78ac06f + 37aac50 commit c3a7faf
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 6 deletions.
115 changes: 115 additions & 0 deletions docs/zh-CN/components/form/input-signature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
title: inputSignature 签名面板
description:
type: 0
group: null
menuName: inputSignature
icon:
order: 62
---

## 基本用法

```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"name": "signature",
"type": "input-signature",
"label": "手写签名",
"height": 200
}
]
}
```

## 水平展示

```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"name": "signature",
"type": "input-signature",
"horiz": true,
"height": 300
}
]
}
```

## 自定义按钮名称

```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"name": "signature",
"type": "input-signature",
"height": 160,
"confirmText": "确定",
"undoText": "上一步",
"clearText": "重置"
}
]
}
```

## 自定义颜色

```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"name": "signature",
"type": "input-signature",
"label": "手写签名",
"height": 200,
"color": "#ff0000",
"bgColor": "#fff"
}
]
}
```

## 配合图片组件实现实时预览

```schema: scope="body"
{
"type": "form",
"api": "/api/mock2/form/saveForm",
"body": [
{
"name": "signature",
"type": "input-signature",
"label": "手写签名",
"height": 200
},
{
"type": "image",
"name": "signature"
}
]
}
```

## 属性表

| 属性名 | 类型 | 默认值 | 说明 |
| ----------- | --------- | --------- | ------------------ |
| width | `number` | | 组件宽度,最小 300 |
| height | `number` | | 组件高度,最小 160 |
| color | `string` | `#000` | 手写字体颜色 |
| bgColor | `string` | `#EFEFEF` | 面板背景颜色 |
| clearText | `string` | `清空` | 清空按钮名称 |
| undoText | `string` | `撤销` | 撤销按钮名称 |
| confirmText | `string` | `确认` | 确认按钮名称 |
| horiz | `boolean` | | 是否水平展示 |
10 changes: 10 additions & 0 deletions examples/components/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,16 @@ export const components = [
wrapDoc
)
)
},

{
label: 'InputSignature 签名面板',
path: '/zh-CN/components/form/input-signature',
component: React.lazy(() =>
import('../../docs/zh-CN/components/form/input-signature.md').then(
wrapDoc
)
)
}
]
},
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"dependencies": {
"path-to-regexp": "^6.2.0",
"postcss": "^8.4.14",
"qs": "6.9.7"
"qs": "6.9.7",
"smooth-signature": "^1.0.13"
},
"devDependencies": {
"@babel/generator": "^7.22.9",
Expand Down Expand Up @@ -149,4 +150,4 @@
"printBasicPrototype": false
}
}
}
}
35 changes: 35 additions & 0 deletions packages/amis-ui/scss/components/_signature.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.#{$ns}Signature {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
&-canvas {
border: 1px dashed #aaa;
}
&-Tool {
text-align: center;
height: 40px;
margin-top: 8px;
button {
margin-left: 5px;
}
}

&.is-horizontal {
flex-direction: row-reverse;
align-items: center;
.#{$ns}Signature-Tool {
width: 32px;
height: 100%;
margin-top: 0;
display: flex;
justify-content: center;
align-items: center;
.actions {
white-space: nowrap;
transform: rotate(90deg);
}
}
}
}
1 change: 1 addition & 0 deletions packages/amis-ui/scss/themes/_common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,6 @@
@import '../components/menu';
@import '../components/overflow-tpl';
@import '../components/pdf_viewer';
@import '../components/signature';

@import '../components/print';
113 changes: 113 additions & 0 deletions packages/amis-ui/src/components/Signature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @file Signature.tsx 签名组件
*
* @created: 2024/03/04
*/

import React from 'react';
import {themeable, ThemeProps} from 'amis-core';
import {LocaleProps, localeable} from 'amis-core';
import {resizeSensor} from 'amis-core';
import SmoothSignature from 'smooth-signature';
import Button from './Button';

export interface ISignatureProps extends LocaleProps, ThemeProps {
width?: number;
height?: number;
color?: string;
bgColor?: string;
clearText?: string;
undoText?: string;
confirmText?: string;
horizontal?: boolean;
onChange?: (value?: string) => void;
}

const Signature: React.FC<ISignatureProps> = props => {
const {classnames: cx, horizontal, width, height} = props;
const [sign, setSign] = React.useState<SmoothSignature | null>(null);
const wrapper = React.useRef<HTMLDivElement>(null);
const canvas = React.useRef<HTMLCanvasElement>(null);

React.useEffect(() => {
if (!wrapper.current || !canvas.current) {
return;
}

initCanvas(canvas.current);
const unSensor = resizeSensor(wrapper.current, resize);

return () => {
setSign(null);
unSensor();
};
}, []);

const clear = React.useCallback(() => {
if (sign) {
sign.clear();
props.onChange?.(undefined);
}
}, [sign]);
const undo = React.useCallback(() => {
if (sign) {
sign.undo();
}
}, [sign]);
const confirm = React.useCallback(() => {
if (sign) {
const base64 = sign.toDataURL();
props.onChange?.(base64);
}
}, [sign]);
const resize = React.useCallback(() => {
setSign(null);
initCanvas(canvas.current!);
}, []);
const initCanvas = React.useCallback(
(element: HTMLCanvasElement) => {
const {width, height} = props;
const rect = element.parentElement!.getBoundingClientRect();
const clientWidth = Math.floor(rect.width);
const defaultWidth = width || clientWidth - (horizontal ? 40 : 0);
const defaultHeight = height || clientWidth / 2 - (horizontal ? 0 : 40);
const signature = new SmoothSignature(element, {
width: Math.max(defaultWidth, 300),
height: Math.max(defaultHeight, 160),
color: props.color || '#000',
bgColor: props.bgColor || '#efefef'
});
setSign(signature);
},
[width, height, horizontal]
);

function renderTool() {
const {translate: __, clearText, undoText, confirmText} = props;
return (
<div className={cx('Signature-Tool')}>
<div className="actions">
<Button onClick={clear}>{clearText || __('Signature.clear')}</Button>
<Button onClick={undo}>{undoText || __('Signature.undo')}</Button>
<Button onClick={confirm}>
{confirmText || __('Signature.confirm')}
</Button>
</div>
</div>
);
}

return (
<div
className={cx('Signature', {
'is-horizontal': horizontal
})}
ref={wrapper}
>
<canvas className={cx('Signature-canvas')} ref={canvas} />
{renderTool()}
</div>
);
};

export default themeable(localeable(Signature));
4 changes: 3 additions & 1 deletion packages/amis-ui/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ import InputBoxWithSuggestion from './InputBoxWithSuggestion';
import {CodeMirrorEditor} from './CodeMirror';
import type CodeMirror from 'codemirror';
import OverflowTpl from './OverflowTpl';
import Signature from './Signature';

export {
NotFound,
Expand Down Expand Up @@ -263,5 +264,6 @@ export {
Menu,
CodeMirror,
CodeMirrorEditor,
OverflowTpl
OverflowTpl,
Signature
};
5 changes: 4 additions & 1 deletion packages/amis-ui/src/locale/de-DE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,5 +430,8 @@ register('de-DE', {
'TimeNow': 'Jetzt',
'Steps.step': 'Schritt {{index}}',
'FormulaInput.True': 'Treu',
'FormulaInput.False': 'Falsch'
'FormulaInput.False': 'Falsch',
'Signature.clear': 'leer',
'Signature.undo': 'widerrufen',
'Signature.confirm': 'bestätigen'
});
5 changes: 4 additions & 1 deletion packages/amis-ui/src/locale/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,5 +414,8 @@ register('en-US', {
'IconSelect.choice': 'Icon selection',
'Steps.step': 'Step {{index}}',
'FormulaInput.True': 'True',
'FormulaInput.False': 'False'
'FormulaInput.False': 'False',
'Signature.clear': 'clear',
'Signature.undo': 'undo',
'Signature.confirm': 'confirm'
});
5 changes: 4 additions & 1 deletion packages/amis-ui/src/locale/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,5 +409,8 @@ register('zh-CN', {
'IconSelect.choice': '图标选择',
'Steps.step': '第 {{index}} 步',
'FormulaInput.True': '真',
'FormulaInput.False': '假'
'FormulaInput.False': '假',
'Signature.clear': '清空',
'Signature.undo': '撤销',
'Signature.confirm': '确认'
});
3 changes: 3 additions & 0 deletions packages/amis/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ import {TransferPickerControlSchema} from './renderers/Form/TransferPicker';
import {TabsTransferPickerControlSchema} from './renderers/Form/TabsTransferPicker';
import {UserSelectControlSchema} from './renderers/Form/UserSelect';
import {JSONSchemaEditorControlSchema} from './renderers/Form/JSONSchemaEditor';
import {InputSignatureSchema} from './renderers/Form/InputSignature';
import {TableSchema2} from './renderers/Table2';
import {
BaseSchemaWithoutType,
Expand Down Expand Up @@ -254,6 +255,7 @@ export type SchemaType =
| 'diff-editor'
| 'office-viewer'
| 'pdf-viewer'
| 'input-signature'

// editor 系列
| 'editor'
Expand Down Expand Up @@ -461,6 +463,7 @@ export type SchemaObject =
| InputGroupControlSchema
| ListControlSchema
| JSONSchemaEditorControlSchema
| InputSignatureSchema
| LocationControlSchema
| UUIDControlSchema
| MatrixControlSchema
Expand Down
1 change: 1 addition & 0 deletions packages/amis/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import './renderers/Form/TabsTransferPicker';
import './renderers/Form/Group';
import './renderers/Form/InputGroup';
import './renderers/Form/UserSelect';
import './renderers/Form/InputSignature';
import './renderers/Grid';
import './renderers/Grid2D';
import './renderers/HBox';
Expand Down

0 comments on commit c3a7faf

Please sign in to comment.