feat:action 支持设置提交后倒计时 (#1607)

This commit is contained in:
吴多益 2021-03-02 21:40:29 +08:00 committed by GitHub
parent 696c3e4dc4
commit f25fbbf41e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 124 additions and 5 deletions

View File

@ -319,6 +319,34 @@ icon 也可以是 url 地址,比如
| feedback | `DialogObject` | - | 如果 ajax 类型的,当 ajax 返回正常后,还能接着弹出一个 dialog 做其他交互。返回的数据可用于这个 dialog 中。格式可参考[Dialog](./Dialog.md) |
| messages | `object` | - | `success`ajax 操作成功后提示,可以不指定,不指定时以 api 返回为准。`failed`ajax 操作失败提示。 |
### 倒计时
主要用于发验证码的场景,通过设置倒计时 `countDown`(单位是秒),让点击按钮后禁用一段时间:
```schema: scope="body"
{
"type": "form",
"controls": [
{
"name": "phone",
"type": "text",
"required": true,
"label": "手机号",
"addOn": {
"label": "发送验证码",
"type": "button",
"countDown": 60,
"countDownTpl": "${timeLeft} 秒后重发",
"actionType": "ajax",
"api": "https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/mock2/form/saveForm?phone=${phone}"
}
}
]
}
```
同时还能通过 `countDownTpl` 来控制显示的文本,其中 `${timeLeft}` 变量是剩余时间。
## 跳转链接
### 单页跳转

View File

@ -1,6 +1,7 @@
import {register} from '../locale';
register('en-US', {
'Action.countDown': 'Wait for ${timeLeft}s',
'Alert.info': 'System Info',
'asc': 'Asc',
'cancel': 'Cancel',

View File

@ -1,6 +1,7 @@
import {register} from '../locale';
register('zh-CN', {
'Action.countDown': '请等待 ${timeLeft} 秒',
'Alert.info': '系统消息',
'asc': '正序',
'cancel': '取消',

View File

@ -99,6 +99,16 @@ export interface ButtonSchema extends BaseSchema {
*
*/
target?: string;
/**
*
*/
countDown?: number;
/**
*
*/
countDownTpl?: string;
}
export interface AjaxActionSchema extends ButtonSchema {
@ -324,19 +334,53 @@ export interface ActionProps
const allowedType = ['button', 'submit', 'reset'];
export class Action extends React.Component<ActionProps> {
interface ActionState {
inCountDown: boolean; // 是否在倒计时
countDownEnd: number; // 倒计时结束的精确时间
timeLeft: number; // 倒计时剩余时间
}
export class Action extends React.Component<ActionProps, ActionState> {
static defaultProps = {
type: 'button' as 'button',
componentClass: 'button' as React.ReactType,
tooltipPlacement: 'bottom' as 'bottom',
activeClassName: 'is-active'
activeClassName: 'is-active',
countDownTpl: 'Action.countDown',
countDown: 0
};
state: ActionState = {
inCountDown: false,
countDownEnd: 0,
timeLeft: 0
};
localStorageKey: string;
dom: any;
constructor(props: ActionProps) {
super(props);
this.localStorageKey = 'amis-countdownend-' + (this.props.name || '');
const countDownEnd = parseInt(
localStorage.getItem(this.localStorageKey) || '0'
);
if (countDownEnd && this.props.countDown) {
if (Date.now() < countDownEnd) {
this.state = {
inCountDown: true,
countDownEnd,
timeLeft: Math.floor((countDownEnd - Date.now()) / 1000)
};
this.handleCountDown();
}
}
}
@autobind
handleAction(e: React.MouseEvent<any>) {
const {onAction, onClick, disabled} = this.props;
const {onAction, onClick, disabled, countDown} = this.props;
const result: any = onClick && onClick(e, this.props);
@ -347,18 +391,51 @@ export class Action extends React.Component<ActionProps> {
e.preventDefault();
const action = pick(this.props, ActionProps) as ActionSchema;
onAction(e, action);
if (countDown) {
const countDownEnd = Date.now() + countDown * 1000;
this.setState({
countDownEnd: countDownEnd,
inCountDown: true,
timeLeft: countDown
});
localStorage.setItem(this.localStorageKey, String(countDownEnd));
setTimeout(() => {
this.handleCountDown();
}, 1000);
}
}
@autobind
handleCountDown() {
// setTimeout 一般会晚于 1s经过几十次后就不准了所以使用真实时间进行 diff
const timeLeft = Math.floor((this.state.countDownEnd - Date.now()) / 1000);
if (timeLeft <= 0) {
this.setState({
inCountDown: false,
timeLeft: timeLeft
});
} else {
this.setState({
timeLeft: timeLeft
});
setTimeout(() => {
this.handleCountDown();
}, 1000);
}
}
render() {
const {
type,
label,
icon,
iconClassName,
primary,
size,
level,
disabled,
countDownTpl,
block,
className,
componentClass,
@ -368,6 +445,7 @@ export class Action extends React.Component<ActionProps> {
actionType,
link,
data,
translate: __,
activeClassName,
isCurrentUrl,
isMenuItem,
@ -377,12 +455,23 @@ export class Action extends React.Component<ActionProps> {
classnames: cx
} = this.props;
let label = this.props.label;
let disabled = this.props.disabled;
let isActive = !!active;
if (actionType === 'link' && !isActive && link && isCurrentUrl) {
isActive = isCurrentUrl(link);
}
// 倒计时
if (this.state.inCountDown) {
label = filterContents(__(countDownTpl), {
...data,
timeLeft: this.state.timeLeft
}) as string;
disabled = true;
}
const iconElement = generateIcon(cx, icon, 'Button-icon', iconClassName);
return isMenuItem ? (