mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-05 05:18:34 +08:00
Merge pull request #104 from catchonme/master
统一API验证方式 audio 支持src取变量值
This commit is contained in:
commit
b167e86ecd
@ -63,14 +63,17 @@ button-group 有两种模式,除了能让按钮组合在一起,还能做类
|
||||
- `options` 选项配置,类型为数组,成员格式如下。
|
||||
- `label` 文字
|
||||
- `value` 值
|
||||
- `image` 图片的 http 地址。
|
||||
- `image` 图片的 `http` 地址。
|
||||
- `source` Api 地址,如果选项不固定,可以通过配置 `source` 动态拉取。
|
||||
- `multiple` 默认为 `false`, 设置成 `true` 表示可多选。
|
||||
- `joinValues` 默认为 `true`
|
||||
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
|
||||
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
|
||||
- `delimiter` 默认为 `,`
|
||||
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 `value` 的值封装为数组,作为当前表单项的值。
|
||||
- `clearable` 默认为 `true`, 表示可以取消选中。
|
||||
- `size` 按钮大小,从小到大依次为`xs, sm, md, lg`
|
||||
- `disabled` 是否禁用`options` 中选项
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
```schema:height="250" scope="form"
|
@ -7,7 +7,11 @@
|
||||
- `label` 文字
|
||||
- `value` 值
|
||||
- `source` Api 地址,如果选项不固定,可以通过配置 `source` 动态拉取。另外也可以用 `$xxxx` 来获取当前作用域中的变量。
|
||||
更多配置请参考 [FormItem](./FormItem.md)。
|
||||
- `joinValues` 默认为 `true`
|
||||
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
|
||||
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
|
||||
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 `value` 的值封装为数组,作为当前表单项的值。
|
||||
- `delimiter` 默认为 `,`
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
```schema:height="300" scope="form-item"
|
||||
|
@ -21,8 +21,12 @@
|
||||
- `addButtonText` 新增按钮文字,默认为 `"新增"`。
|
||||
- `minLength` 限制最小长度。
|
||||
- `maxLength` 限制最大长度。
|
||||
- `scaffold` 单条初始值。默认为 `{}`。
|
||||
- `scaffold` 单组表单项初始值。默认为 `{}`。
|
||||
- `canAccessSuperData` 指定是否可以自动获取上层的数据并映射到表单项上,默认是`false`。
|
||||
- `conditions` 数组的形式包含所有条件的渲染类型,单个数组内的`test` 为判断条件,数组内的`controls`为符合该条件后渲染的`schema`
|
||||
- `typeSwitchable` 是否可切换条件,配合`conditions`使用
|
||||
- `formClassName` 单组表单项的类名
|
||||
- `noBorder` 单组表单项是否有边框
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
```schema:height="450" scope="form"
|
||||
|
@ -7,14 +7,12 @@
|
||||
- `inputFormat` 默认 `YYYY-MM-DD HH:mm:ss` 用来配置显示的时间格式。
|
||||
- `placeholder` 默认 `请选择日期`
|
||||
- `timeConstraints` 请参考: [react-datetime](https://github.com/YouCanBookMe/react-datetime)
|
||||
- `value` 这里面 value 需要特殊说明一下,因为支持相对值。如:
|
||||
- `value` 这里面 `value` 需要特殊说明一下,因为支持相对值。如:
|
||||
- `-2mins` 2 分钟前
|
||||
- `+2days` 2 天后
|
||||
- `-10week` 十周前
|
||||
- `minDate` 限制最小日期,可用 `${xxx}` 取值,或者输入相对时间,或者时间戳。如:`${start}`、`+3days`、`+3days+2hours`或者 `${start|default:-2days}+3days`
|
||||
- `maxDate` 限制最大日期,可用 `${xxx}` 取值,或者输入相对时间,或者时间戳。如:`${start}`、`+3days`、`+3days+2hours`或者 `${start|default:-2days}+3days`
|
||||
- `minTime` 限制最小时间,可用 `${xxx}` 取值,或者输入相对时间,或者时间戳。如:`${start}`、`+3days`、`+3days+2hours`或者 `${start|default:-2days}+3days`
|
||||
- `maxTime` 限制最大时间,可用 `${xxx}` 取值,或者输入相对时间,或者时间戳。如:`${start}`、`+3days`、`+3days+2hours`或者 `${start|default:-2days}+3days`
|
||||
|
||||
可用单位: `min`、`hour`、`day`、`week`、`month`、`year`。所有单位支持复数形式。
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
|
||||
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
|
||||
- `delimiter` 默认为 `,`
|
||||
- `clearable` 默认为 `true`, 表示可以取消选中。
|
||||
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 value 的值封装为数组,作为当前表单项的值。
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
单选
|
||||
|
@ -7,6 +7,8 @@
|
||||
- `rows` 行信息, 数组中 `label` 字段是必须给出的
|
||||
- `rowLabel` 行标题说明
|
||||
- `source` Api 地址,如果选项不固定,可以通过配置 `source` 动态拉取。
|
||||
- `multiple` 多选,默认为 `true`
|
||||
- `singleSelectMode` 设置单选模式,`multiple`为`false`时有效,可设置为`cell`, `row`, `column` 分别为全部选项中只能单选某个单元格、每行只能单选某个单元格,每列只能单选某个单元格
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
```schema:height="250" scope="form-item"
|
||||
|
@ -12,11 +12,11 @@
|
||||
- `joinValues` 默认为 `true`
|
||||
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
|
||||
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
|
||||
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 value 的值封装为数组,作为当前表单项的值。
|
||||
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 `value` 的值封装为数组,作为当前表单项的值。
|
||||
- `delimiter` 默认为 `,`
|
||||
- `clearable` 默认为 `false`, 当设置为 `true` 时,已选中的选项右侧会有个小 `X` 用来取消设置。
|
||||
- `searchable` 默认为 `false`,表示可以通过输入部分内容检索出选项。
|
||||
- `checkall` 默认为 `false` 开启后支持全选
|
||||
- `checkAll` 默认为 `false` 开启后支持全选
|
||||
- `checkAllLabel` 默认为 `全选`, 全选的文字
|
||||
- `defaultCheckAll` 是否默认全选,默认为`false`
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
@ -17,6 +17,11 @@
|
||||
- `source` 通过 `options` 只能配置静态数据,如果设置了 `source` 则会从接口拉取,实现动态效果。
|
||||
- `autoComplete` 跟 `source` 不同的是,每次用户输入都会去接口获取提示。
|
||||
- `multiple` 默认为 `false`, 设置成 `true` 表示可多选。
|
||||
- `joinValues` 默认为 `true`
|
||||
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
|
||||
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
|
||||
- `delimiter` 默认为 `,`
|
||||
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 value 的值封装为数组,作为当前表单项的值。
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
```schema:height="200" scope="form-item"
|
||||
|
@ -5,17 +5,13 @@
|
||||
- `type` 请设置成 `time`
|
||||
- `format` 默认 `X` 即时间戳格式,用来提交的时间格式。更多格式类型请参考 [moment](http://momentjs.com/).
|
||||
- `inputFormat` 默认 `HH:mm` 用来配置显示的时间格式。
|
||||
- `timeFormat` 默认 `HH:mm` 用来配置选择的时间格式。
|
||||
- `placeholder` 默认 `请选择日期`
|
||||
- `timeConstraints` 请参考: [react-datetime](https://github.com/YouCanBookMe/react-datetime)
|
||||
- `value` 这里面 value 需要特殊说明一下,因为支持相对值。如:
|
||||
- `-2mins` 2 分钟前
|
||||
- `+2days` 2 天后
|
||||
- `-10week` 十周前
|
||||
- `minTime` 限制最小时间,可用 `${xxx}` 取值,或者输入相对时间,或者时间戳。如:`${start}`、`+3days`、`+3days+2hours`或者 `${start|default:-2days}+3days`
|
||||
- `maxTime` 限制最大时间,可用 `${xxx}` 取值,或者输入相对时间,或者时间戳。如:`${start}`、`+3days`、`+3days+2hours`或者 `${start|default:-2days}+3days`
|
||||
|
||||
可用单位: `min`、`hour`、`day`、`week`、`month`、`year`。所有单位支持复数形式。
|
||||
|
||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||
|
||||
```schema:height="250" scope="form"
|
||||
|
@ -17,6 +17,8 @@
|
||||
}
|
||||
|
||||
.#{$ns}Carousel {
|
||||
min-width: $Carousel-minWidth;
|
||||
height: $Carousel-height;
|
||||
position: relative;
|
||||
display: block;
|
||||
background: $Carousel-bg;
|
||||
@ -56,8 +58,8 @@
|
||||
}
|
||||
|
||||
&-container {
|
||||
min-width: $Carousel-minWidth;
|
||||
height: $Carousel-height;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
|
@ -4,6 +4,7 @@ import {Renderer, RendererProps} from '../factory';
|
||||
import {autobind} from '../utils/helper';
|
||||
import {volumeIcon, muteIcon, playIcon, pauseIcon} from '../components/icons';
|
||||
import {resolveVariable} from '../utils/tpl-builtin';
|
||||
import {filter} from '../utils/tpl';
|
||||
|
||||
export interface AudioProps extends RendererProps {
|
||||
className?: string;
|
||||
@ -49,7 +50,7 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||
};
|
||||
|
||||
state: AudioState = {
|
||||
src: this.props.value || this.props.src || resolveVariable(this.props.name, this.props.data) || '',
|
||||
src: this.props.value || (this.props.src ? filter(this.props.src, this.props.data) : '') || resolveVariable(this.props.name, this.props.data) || '',
|
||||
isReady: false,
|
||||
muted: false,
|
||||
playing: false,
|
||||
@ -82,9 +83,12 @@ export class Audio extends React.Component<AudioProps, AudioState> {
|
||||
componentWillReceiveProps(nextProps:AudioProps) {
|
||||
const props = this.props;
|
||||
|
||||
if (props.value !== nextProps.value || props.src !== nextProps.src) {
|
||||
if (
|
||||
props.value !== nextProps.value ||
|
||||
filter(props.src as string, props.data) !== filter(nextProps.src as string, nextProps.data)
|
||||
) {
|
||||
this.setState({
|
||||
src: nextProps.value || nextProps.src,
|
||||
src: nextProps.value || filter(nextProps.src as string, nextProps.data),
|
||||
playing: false
|
||||
}, () => {
|
||||
this.audio.load();
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
isObjectShallowModified,
|
||||
noop,
|
||||
isVisible,
|
||||
getVariable,
|
||||
getVariable
|
||||
} from '../utils/helper';
|
||||
import {observer} from 'mobx-react';
|
||||
import partition = require('lodash/partition');
|
||||
@ -22,7 +22,7 @@ import pick = require('lodash/pick');
|
||||
import qs from 'qs';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {evalExpression, filter} from '../utils/tpl';
|
||||
import {isValidApi, buildApi} from '../utils/api';
|
||||
import {isValidApi, buildApi, isEffectiveApi} from '../utils/api';
|
||||
import omit = require('lodash/omit');
|
||||
import find = require('lodash/find');
|
||||
import Html from '../components/Html';
|
||||
@ -278,23 +278,24 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
// 由于 ajax 一段时间后再弹出,肯定被浏览器给阻止掉的,所以提前弹。
|
||||
action.redirect && action.blank && env.jumpTo(filter(action.redirect, data), action);
|
||||
|
||||
return store
|
||||
.saveRemote(action.api as string, data, {
|
||||
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
|
||||
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed),
|
||||
})
|
||||
.then(async (payload: object) => {
|
||||
const data = createObject(ctx, payload);
|
||||
return isEffectiveApi(action.api, data) &&
|
||||
store
|
||||
.saveRemote(action.api, data, {
|
||||
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
|
||||
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed),
|
||||
})
|
||||
.then(async (payload: object) => {
|
||||
const data = createObject(ctx, payload);
|
||||
|
||||
if (action.feedback && isVisible(action.feedback, data)) {
|
||||
await this.openFeedback(action.feedback, data);
|
||||
stopAutoRefreshWhenModalIsOpen && clearTimeout(this.timer);
|
||||
}
|
||||
if (action.feedback && isVisible(action.feedback, data)) {
|
||||
await this.openFeedback(action.feedback, data);
|
||||
stopAutoRefreshWhenModalIsOpen && clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
action.redirect && !action.blank && env.jumpTo(filter(action.redirect, data), action);
|
||||
action.reload ? this.reloadTarget(action.reload, data) : this.search(undefined, undefined, true);
|
||||
})
|
||||
.catch(() => {});
|
||||
action.redirect && !action.blank && env.jumpTo(filter(action.redirect, data), action);
|
||||
action.reload ? this.reloadTarget(action.reload, data) : this.search(undefined, undefined, true);
|
||||
})
|
||||
.catch(() => {});
|
||||
} else if (pickerMode && (action.actionType === 'confirm' || action.actionType === 'submit')) {
|
||||
return Promise.resolve({
|
||||
items: store.selectedItems.concat(),
|
||||
@ -334,22 +335,23 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
ctx
|
||||
);
|
||||
} else if (action.actionType === 'ajax') {
|
||||
store
|
||||
.saveRemote(action.api as string, ctx, {
|
||||
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
|
||||
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed),
|
||||
})
|
||||
.then(async () => {
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
stopAutoRefreshWhenModalIsOpen && clearTimeout(this.timer);
|
||||
}
|
||||
isEffectiveApi(action.api, ctx) &&
|
||||
store
|
||||
.saveRemote(action.api as string, ctx, {
|
||||
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
|
||||
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveFailed),
|
||||
})
|
||||
.then(async () => {
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
stopAutoRefreshWhenModalIsOpen && clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
action.reload
|
||||
? this.reloadTarget(action.reload, store.data)
|
||||
: this.search({[pageField || 'page']: 1}, undefined, true);
|
||||
})
|
||||
.catch(() => null);
|
||||
action.reload
|
||||
? this.reloadTarget(action.reload, store.data)
|
||||
: this.search({[pageField || 'page']: 1}, undefined, true);
|
||||
})
|
||||
.catch(() => null);
|
||||
} else if (onAction) {
|
||||
onAction(e, action, ctx);
|
||||
}
|
||||
@ -562,8 +564,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
);
|
||||
this.lastQuery = store.query;
|
||||
const data = createObject(store.data, store.query);
|
||||
api &&
|
||||
(!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, data)) &&
|
||||
isEffectiveApi(api, data) &&
|
||||
store
|
||||
.fetchInitData(api, data, {
|
||||
successMessage: messages && messages.fetchSuccess,
|
||||
@ -652,18 +653,19 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
data.unModifiedItems = unModifiedItems;
|
||||
}
|
||||
|
||||
store
|
||||
.saveRemote(quickSaveApi, data, {
|
||||
successMessage: messages && messages.saveFailed,
|
||||
errorMessage: messages && messages.saveSuccess,
|
||||
})
|
||||
.then(() => {
|
||||
if ((quickSaveApi as ApiObject).reload) {
|
||||
this.reloadTarget((quickSaveApi as ApiObject).reload as string, data);
|
||||
}
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
isEffectiveApi(quickSaveApi, store.data) &&
|
||||
store
|
||||
.saveRemote(quickSaveApi, data, {
|
||||
successMessage: messages && messages.saveFailed,
|
||||
errorMessage: messages && messages.saveSuccess,
|
||||
})
|
||||
.then(() => {
|
||||
if ((quickSaveApi as ApiObject).reload) {
|
||||
this.reloadTarget((quickSaveApi as ApiObject).reload as string, data);
|
||||
}
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
if (!quickSaveItemApi) {
|
||||
env && env.alert('CRUD quickSaveItemApi is required!');
|
||||
@ -675,15 +677,16 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
modified: diff,
|
||||
});
|
||||
|
||||
store
|
||||
.saveRemote(quickSaveItemApi, createObject(data, rows))
|
||||
.then(() => {
|
||||
if ((quickSaveItemApi as ApiObject).reload) {
|
||||
this.reloadTarget((quickSaveItemApi as ApiObject).reload as string, data);
|
||||
}
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
isEffectiveApi(quickSaveItemApi, store.data) &&
|
||||
store
|
||||
.saveRemote(quickSaveItemApi, createObject(data, rows))
|
||||
.then(() => {
|
||||
if ((quickSaveItemApi as ApiObject).reload) {
|
||||
this.reloadTarget((quickSaveItemApi as ApiObject).reload as string, data);
|
||||
}
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -761,16 +764,17 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
hasIdField && (model.ids = rows.map((item: any) => item[primaryField as string]).join(','));
|
||||
hasIdField && orderField && (model.order = rows.map(item => pick(item, [primaryField as string, orderField])));
|
||||
|
||||
store
|
||||
.saveRemote(saveOrderApi, model)
|
||||
.then(() => {
|
||||
if ((saveOrderApi as ApiObject).reload) {
|
||||
this.reloadTarget((saveOrderApi as ApiObject).reload as string, model);
|
||||
}
|
||||
isEffectiveApi(saveOrderApi, store.data) &&
|
||||
store
|
||||
.saveRemote(saveOrderApi, model)
|
||||
.then(() => {
|
||||
if ((saveOrderApi as ApiObject).reload) {
|
||||
this.reloadTarget((saveOrderApi as ApiObject).reload as string, model);
|
||||
}
|
||||
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
this.search();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
handleSelect(items: Array<any>, unSelectedItems: Array<any>) {
|
||||
@ -970,7 +974,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
`bulk-action/${index}`,
|
||||
{
|
||||
size: 'sm',
|
||||
...omit(btn, ['visibileOn', 'hiddenOn', 'disabledOn']),
|
||||
...omit(btn, ['visibleOn', 'hiddenOn', 'disabledOn']),
|
||||
type: 'button',
|
||||
},
|
||||
{
|
||||
@ -991,7 +995,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
`bulk-action/${index}`,
|
||||
{
|
||||
size: 'sm',
|
||||
...omit(btn, ['visibileOn', 'hiddenOn', 'disabledOn']),
|
||||
...omit(btn, ['visibleOn', 'hiddenOn', 'disabledOn']),
|
||||
type: 'button',
|
||||
},
|
||||
{
|
||||
|
@ -262,7 +262,6 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
className={cx('Carousel-container')}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
style={carouselStyles}
|
||||
>
|
||||
{options.map((option:any, key:number) => (
|
||||
<Transition
|
||||
@ -294,7 +293,7 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(`Carousel Carousel--${controlsTheme}`, className)}>
|
||||
<div className={cx(`Carousel Carousel--${controlsTheme}`, className)} style={carouselStyles}>
|
||||
{body ? body : placeholder}
|
||||
</div>
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ import cx from 'classnames';
|
||||
import LazyComponent from '../components/LazyComponent';
|
||||
import {resizeSensor} from '../utils/resize-sensor';
|
||||
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
|
||||
import {isApiOutdated} from '../utils/api';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||
|
||||
export interface ChartProps extends RendererProps {
|
||||
@ -128,7 +128,7 @@ export class Chart extends React.Component<ChartProps> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (api && (api as ApiObject).sendOn && !evalExpression((api as ApiObject).sendOn as string, store.data)) {
|
||||
if (!isEffectiveApi(api, store.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,11 @@ import {
|
||||
OptionsControlProps,
|
||||
Option} from './Options';
|
||||
import Select from '../../components/Select';
|
||||
import {Api} from '../../types';
|
||||
import {isEffectiveApi} from '../../utils/api';
|
||||
|
||||
export interface ChainedSelectProps extends OptionsControlProps {
|
||||
autoComplete?: string;
|
||||
autoComplete?: Api;
|
||||
searchable?: boolean;
|
||||
};
|
||||
|
||||
@ -83,7 +85,7 @@ export default class ChainedSelectControl extends React.Component<ChainedSelectP
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (!arr[idx] || !env || !source) {
|
||||
if (!arr[idx] || !env || !source || !isEffectiveApi(source, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6,11 +6,8 @@ import {
|
||||
import cx from 'classnames';
|
||||
import ColorPicker from '../../components/ColorPicker';
|
||||
|
||||
|
||||
export interface ColorProps extends FormControlProps {
|
||||
placeholder?: string;
|
||||
inputFormat?: string;
|
||||
timeFormat?: string;
|
||||
format?: string;
|
||||
timeConstrainst?: object;
|
||||
closeOnSelect?:boolean;
|
||||
|
@ -21,6 +21,7 @@ import { evalExpression, filter } from '../../utils/tpl';
|
||||
import find = require('lodash/find');
|
||||
import Select from '../../components/Select';
|
||||
import { dataMapping } from '../../utils/tpl-builtin';
|
||||
import { isEffectiveApi } from '../../utils/api';
|
||||
|
||||
export interface Condition {
|
||||
test: string;
|
||||
@ -245,6 +246,10 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEffectiveApi(deleteApi, ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await env.fetcher(deleteApi, ctx);
|
||||
|
||||
if (!result.ok) {
|
||||
|
@ -115,9 +115,9 @@ export class DatetimeControlRenderer extends DateControl {
|
||||
static defaultProps = {
|
||||
...DateControl.defaultProps,
|
||||
placeholder: '请选择日期以及时间',
|
||||
inputFormat: 'YYYY-MM-DD HH:mm',
|
||||
inputFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
dateFormat: 'LL',
|
||||
timeFormat: 'HH:mm',
|
||||
timeFormat: 'HH:mm:ss',
|
||||
closeOnSelect: false,
|
||||
strictMode: false
|
||||
};
|
||||
|
@ -630,7 +630,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
|
||||
<div className="clear">
|
||||
{multiple && (!maxLength || files.length < maxLength) || !multiple && !files.length ? (
|
||||
<label className={cx("btn m-r-xs", btnClassName, {disabled})}>
|
||||
<input type="file" accept={accept} multiple={multiple} className="invisible" onChange={this.handleDrop} />
|
||||
<input type="file" accept={accept} disabled={disabled} multiple={multiple} className="invisible" onChange={this.handleDrop} />
|
||||
{btnLabel}
|
||||
</label>
|
||||
) : null}
|
||||
|
@ -6,7 +6,7 @@
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { FormControlProps, FormItem } from './Item';
|
||||
import { buildApi, isValidApi } from '../../utils/api';
|
||||
import { buildApi, isValidApi, isEffectiveApi } from '../../utils/api';
|
||||
import { Checkbox } from '../../components';
|
||||
|
||||
export interface Column {
|
||||
@ -104,7 +104,7 @@ export default class MatrixCheckbox extends React.Component<MatrixProps, MatrixS
|
||||
onChange
|
||||
} = this.props;
|
||||
|
||||
if (!source || this.state.loading) {
|
||||
if (!isEffectiveApi(source, data) || this.state.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Api} from '../../types';
|
||||
import {buildApi, isValidApi} from '../../utils/api';
|
||||
import {buildApi, isEffectiveApi, isValidApi} from '../../utils/api';
|
||||
import {
|
||||
anyChanged
|
||||
} from '../../utils/helper';
|
||||
@ -183,7 +183,7 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
let prevApi = buildApi(props.source, props.data as object, {ignoreData: true});
|
||||
let nextApi = buildApi(nextProps.source, nextProps.data as object, {ignoreData: true});
|
||||
|
||||
if (prevApi.url !== nextApi.url && isValidApi(nextApi.url) && (!nextApi.sendOn || evalExpression(nextApi.sendOn, nextProps.data))) {
|
||||
if (prevApi.url !== nextApi.url && isEffectiveApi(nextApi, nextProps.data)) {
|
||||
formItem.loadOptions(nextProps.source, nextProps.data, undefined, true, nextProps.onChange);
|
||||
}
|
||||
}
|
||||
@ -336,7 +336,7 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
onChange
|
||||
} = this.props;
|
||||
|
||||
if (config.autoLoadOptionsFromSource === false || !formItem || !source || source.sendOn && !evalExpression(source.sendOn, data)) {
|
||||
if (config.autoLoadOptionsFromSource === false || !formItem || !isEffectiveApi(source, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,11 @@ import {
|
||||
import Select from '../../components/Select';
|
||||
import find = require('lodash/find');
|
||||
import debouce = require('lodash/debounce');
|
||||
import {Api} from '../../types';
|
||||
import {isEffectiveApi} from '../../utils/api';
|
||||
|
||||
export interface SelectProps extends OptionsControlProps {
|
||||
autoComplete?: string;
|
||||
autoComplete?: Api;
|
||||
searchable?: boolean;
|
||||
};
|
||||
|
||||
@ -110,23 +112,24 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||
|
||||
|
||||
setLoading(true);
|
||||
return env
|
||||
.fetcher(autoComplete as string, {
|
||||
...data,
|
||||
term: input,
|
||||
value: input
|
||||
})
|
||||
.then(ret => {
|
||||
let options = ret.data && (ret.data as any).options || ret.data || [];
|
||||
this.cache[input] = options;
|
||||
let combinedOptions = this.mergeOptions(options);
|
||||
setOptions(combinedOptions);
|
||||
return autoComplete && isEffectiveApi(autoComplete, data) &&
|
||||
env
|
||||
.fetcher(autoComplete, {
|
||||
...data,
|
||||
term: input,
|
||||
value: input
|
||||
})
|
||||
.then(ret => {
|
||||
let options = ret.data && (ret.data as any).options || ret.data || [];
|
||||
this.cache[input] = options;
|
||||
let combinedOptions = this.mergeOptions(options);
|
||||
setOptions(combinedOptions);
|
||||
|
||||
return Promise.resolve({
|
||||
options: combinedOptions,
|
||||
});
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
return Promise.resolve({
|
||||
options: combinedOptions,
|
||||
});
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}
|
||||
|
||||
mergeOptions(options: Array<object>) {
|
||||
@ -188,7 +191,7 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||
value={selectedOptions}
|
||||
options={options}
|
||||
onNewOptionClick={this.handleNewOptionClick}
|
||||
loadOptions={autoComplete ? this.loadRemote : null}
|
||||
loadOptions={isEffectiveApi(autoComplete) ? this.loadRemote : null}
|
||||
creatable={creatable}
|
||||
searchable={autoComplete || creatable ? true : searchable}
|
||||
onChange={this.changeValue}
|
||||
|
@ -7,6 +7,7 @@ import cx from 'classnames';
|
||||
import Button from '../../components/Button';
|
||||
import {createObject, isObjectShallowModified} from '../../utils/helper';
|
||||
import { RendererData, Action, Api, Payload } from '../../types';
|
||||
import { isEffectiveApi } from '../../utils/api';
|
||||
import { filter } from '../../utils/tpl';
|
||||
import omit = require('lodash/omit');
|
||||
import { dataMapping } from '../../utils/tpl-builtin';
|
||||
@ -235,13 +236,15 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
const isNew = !isObjectShallowModified(scaffold, origin, false);
|
||||
|
||||
let remote:Payload | null = null;
|
||||
if (isNew && addApi) {
|
||||
if (isNew && addApi && isEffectiveApi(addApi, createObject(data, item))) {
|
||||
remote = await env.fetcher(addApi, createObject(data, item));
|
||||
} else if (updateApi) {
|
||||
} else if (updateApi && isEffectiveApi(updateApi, createObject(data, item))) {
|
||||
remote = await env.fetcher(updateApi, createObject(data, item));
|
||||
}
|
||||
|
||||
if (remote && !remote.ok) {
|
||||
if (remote === null) {
|
||||
return;
|
||||
} else if (remote && !remote.ok) {
|
||||
env.notify('error', remote.msg || '保存失败');
|
||||
return;
|
||||
} else if (remote && remote.ok) {
|
||||
@ -299,6 +302,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
if (!confirmed) { // 如果不确认,则跳过!
|
||||
return;
|
||||
}
|
||||
if (!isEffectiveApi(deleteApi, ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await env.fetcher(deleteApi, ctx);
|
||||
|
||||
|
@ -13,6 +13,7 @@ import { filter } from '../../utils/tpl';
|
||||
import find = require('lodash/find');
|
||||
import { closeIcon, enterIcon } from '../../components/icons';
|
||||
import { autobind } from '../../utils/helper';
|
||||
import {isEffectiveApi} from '../../utils/api';
|
||||
|
||||
// declare function matchSorter(items:Array<any>, input:any, options:any): Array<any>;
|
||||
|
||||
@ -96,8 +97,7 @@ export default class TextControl extends React.PureComponent<TextProps, TextStat
|
||||
formInited
|
||||
} = this.props;
|
||||
|
||||
if (autoComplete && formItem) {
|
||||
|
||||
if (isEffectiveApi(autoComplete, data) && formItem) {
|
||||
if (formInited) {
|
||||
formItem.loadOptions(autoComplete, {
|
||||
...data,
|
||||
@ -339,7 +339,7 @@ export default class TextControl extends React.PureComponent<TextProps, TextStat
|
||||
data
|
||||
} = this.props;
|
||||
|
||||
if (autoComplete && formItem) {
|
||||
if (isEffectiveApi(autoComplete, data) && formItem) {
|
||||
formItem.loadOptions(autoComplete, {
|
||||
...data,
|
||||
term: this.state.inputValue || formItem.lastSelectValue
|
||||
|
@ -13,10 +13,12 @@ import TreeSelector from '../../components/Tree';
|
||||
import matchSorter from 'match-sorter';
|
||||
import debouce = require('lodash/debounce');
|
||||
import find = require('lodash/find');
|
||||
import {Api} from '../../types';
|
||||
import {isEffectiveApi} from '../../utils/api';
|
||||
|
||||
export interface TreeSelectProps extends OptionsControlProps {
|
||||
placeholder?: any;
|
||||
autoComplete?: string;
|
||||
autoComplete?: Api;
|
||||
};
|
||||
|
||||
export interface TreeSelectState {
|
||||
@ -168,12 +170,13 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||
|
||||
handleInputChange(e:React.ChangeEvent<HTMLInputElement>) {
|
||||
const {
|
||||
autoComplete
|
||||
autoComplete,
|
||||
data
|
||||
} = this.props;
|
||||
|
||||
this.setState({
|
||||
inputValue: e.currentTarget.value
|
||||
}, autoComplete ? () => this.loadRemote(this.state.inputValue) : undefined);
|
||||
}, isEffectiveApi(autoComplete, data) ? () => this.loadRemote(this.state.inputValue) : undefined);
|
||||
}
|
||||
|
||||
handleInputKeyDown(event:React.KeyboardEvent) {
|
||||
@ -230,7 +233,7 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||
setLoading,
|
||||
} = this.props;
|
||||
|
||||
if (!autoComplete) {
|
||||
if (!autoComplete || !isEffectiveApi(autoComplete, data)) {
|
||||
return;
|
||||
} else if (!env || !env.fetcher) {
|
||||
throw new Error('fetcher is required');
|
||||
@ -249,7 +252,7 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||
|
||||
setLoading(true);
|
||||
return env
|
||||
.fetcher(autoComplete as string, {
|
||||
.fetcher(autoComplete, {
|
||||
...data,
|
||||
term: input,
|
||||
value: input
|
||||
@ -344,7 +347,7 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||
autoComplete
|
||||
} = this.props;
|
||||
|
||||
let filtedOptions = !autoComplete && searchable && this.state.inputValue ? this.filterOptions(options, this.state.inputValue) : options;
|
||||
let filtedOptions = !isEffectiveApi(autoComplete) && searchable && this.state.inputValue ? this.filterOptions(options, this.state.inputValue) : options;
|
||||
|
||||
|
||||
return (
|
||||
@ -423,7 +426,7 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||
'TreeSelect--inline': inline,
|
||||
'TreeSelect--single': !multiple,
|
||||
'TreeSelect--multi': multiple,
|
||||
'TreeSelect--searchable': searchable || autoComplete,
|
||||
'TreeSelect--searchable': searchable || isEffectiveApi(autoComplete),
|
||||
'is-opened': this.state.isOpened,
|
||||
'is-focused': this.state.isFocused,
|
||||
'is-disabled': disabled
|
||||
@ -433,7 +436,7 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||
<div className={cx('TreeSelect-valueWrap')}>
|
||||
{this.renderValues()}
|
||||
|
||||
{searchable || autoComplete ? (
|
||||
{searchable || isEffectiveApi(autoComplete) ? (
|
||||
<input
|
||||
onChange={this.handleInputChange}
|
||||
value={this.state.inputValue}
|
||||
|
@ -34,7 +34,7 @@ import Scoped, { ScopedContext, IScopedContext } from '../../Scoped';
|
||||
import { IComboStore } from '../../store/combo';
|
||||
import qs = require('qs');
|
||||
import { dataMapping } from '../../utils/tpl-builtin';
|
||||
import { isApiOutdated } from '../../utils/api';
|
||||
import { isApiOutdated, isEffectiveApi } from '../../utils/api';
|
||||
export type FormGroup = FormSchema & {
|
||||
title?: string;
|
||||
className?: string;
|
||||
@ -75,6 +75,7 @@ export interface FormProps extends RendererProps, FormSchema {
|
||||
asyncApi?: Api; // 如果 api 处理时间过长,可以开启 asyncApi 来处理。轮询检测是否真的完成了。
|
||||
finishedField?: string;
|
||||
initFetch?: boolean; // 是否初始拉取?
|
||||
initFetchOn?: string;
|
||||
className?: string;
|
||||
body?: SchemaNode;
|
||||
wrapWithPanel?: boolean;
|
||||
@ -206,6 +207,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
const {
|
||||
initApi,
|
||||
initFetch,
|
||||
initFetchOn,
|
||||
initAsyncApi,
|
||||
initFinishedField,
|
||||
store,
|
||||
@ -244,19 +246,13 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
initApi && initFetch !== false
|
||||
&& (
|
||||
!(initApi as ApiObject).sendOn
|
||||
|| evalExpression((initApi as ApiObject).sendOn as string, store.data)
|
||||
)
|
||||
) {
|
||||
if (isEffectiveApi(initApi, store.data, initFetch, initFetchOn)) {
|
||||
store
|
||||
.fetchInitData(initApi as any, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed,
|
||||
onSuccess: () => {
|
||||
if (!initAsyncApi || store.data[initFinishedField || 'finished']) {
|
||||
if (!isEffectiveApi(initAsyncApi, store.data) || store.data[initFinishedField || 'finished']) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -276,7 +272,6 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
const props = this.props;
|
||||
const store = props.store;
|
||||
|
||||
|
||||
if (isApiOutdated(prevProps.initApi, props.initApi, prevProps.data, props.data)) {
|
||||
const {
|
||||
fetchSuccess,
|
||||
@ -332,24 +327,26 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
}
|
||||
} = this.props;
|
||||
|
||||
initAsyncApi && store.updateData({
|
||||
[initFinishedField || 'finished']: false
|
||||
});
|
||||
isEffectiveApi(initAsyncApi, store.data) &&
|
||||
store.updateData({
|
||||
[initFinishedField || 'finished']: false
|
||||
});
|
||||
|
||||
initApi && (!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data)) && store.fetchData(initApi, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed,
|
||||
silent,
|
||||
onSuccess: () => {
|
||||
if (!initAsyncApi || store.data[initFinishedField || 'finished']) {
|
||||
return;
|
||||
isEffectiveApi(initApi, store.data) &&
|
||||
store.fetchData(initApi, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed,
|
||||
silent,
|
||||
onSuccess: () => {
|
||||
if (!isEffectiveApi(initAsyncApi, store.data) || store.data[initFinishedField || 'finished']) {
|
||||
return;
|
||||
}
|
||||
|
||||
return until(() => store.checkRemote(initAsyncApi, store.data)
|
||||
, (ret:any) => ret && ret[initFinishedField || 'finished']
|
||||
, (cancel) => this.asyncCancel = cancel);
|
||||
}
|
||||
|
||||
return until(() => store.checkRemote(initAsyncApi, store.data)
|
||||
, (ret:any) => ret && ret[initFinishedField || 'finished']
|
||||
, (cancel) => this.asyncCancel = cancel);
|
||||
}
|
||||
}).then(this.initInterval);
|
||||
}).then(this.initInterval);
|
||||
}
|
||||
|
||||
receive(values:object) {
|
||||
@ -522,34 +519,35 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
} else if (action.api || api) {
|
||||
let finnalAsyncApi = action.asyncApi || asyncApi;
|
||||
|
||||
finnalAsyncApi && store.updateData({
|
||||
isEffectiveApi(finnalAsyncApi, store.data) && store.updateData({
|
||||
[finishedField || 'finished']: false
|
||||
});
|
||||
|
||||
return store
|
||||
.saveRemote(action.api || api as Api, values, {
|
||||
successMessage: saveSuccess,
|
||||
errorMessage: saveFailed,
|
||||
onSuccess: () => {
|
||||
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
||||
return;
|
||||
return isEffectiveApi(action.api || api as Api, store.data) &&
|
||||
store
|
||||
.saveRemote(action.api || api as Api, values, {
|
||||
successMessage: saveSuccess,
|
||||
errorMessage: saveFailed,
|
||||
onSuccess: () => {
|
||||
if (!isEffectiveApi(finnalAsyncApi, store.data) || store.data[finishedField || 'finished']) {
|
||||
return;
|
||||
}
|
||||
|
||||
return until(() => store.checkRemote(finnalAsyncApi as Api, store.data)
|
||||
, (ret:any) => ret && ret[finishedField || 'finished']
|
||||
, (cancel) => this.asyncCancel = cancel);
|
||||
}
|
||||
|
||||
return until(() => store.checkRemote(finnalAsyncApi as Api, store.data)
|
||||
, (ret:any) => ret && ret[finishedField || 'finished']
|
||||
, (cancel) => this.asyncCancel = cancel);
|
||||
}
|
||||
})
|
||||
.then(async (response) => {
|
||||
onSaved && onSaved(values, response);
|
||||
})
|
||||
.then(async (response) => {
|
||||
onSaved && onSaved(values, response);
|
||||
|
||||
// submit 也支持 feedback
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
}
|
||||
// submit 也支持 feedback
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
}
|
||||
|
||||
return values;
|
||||
});
|
||||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(values);
|
||||
@ -585,27 +583,28 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
} else if (action.actionType === 'drawer') {
|
||||
store.openDrawer(data);
|
||||
} else if (action.actionType === 'ajax') {
|
||||
if (!action.api) {
|
||||
if (!isEffectiveApi(action.api)) {
|
||||
return env.alert(`当 actionType 为 ajax 时,请设置 api 属性`);
|
||||
}
|
||||
|
||||
return store
|
||||
.saveRemote(action.api as Api, data, {
|
||||
successMessage: action.messages && action.messages.success || saveSuccess,
|
||||
errorMessage: action.messages && action.messages.failed || saveFailed
|
||||
})
|
||||
.then(async (response) => {
|
||||
response && onChange && onChange(store.data, difference(store.data, store.pristine));
|
||||
store.validated && this.validate(true);
|
||||
return isEffectiveApi(action.api, data) &&
|
||||
store
|
||||
.saveRemote(action.api as Api, data, {
|
||||
successMessage: action.messages && action.messages.success || saveSuccess,
|
||||
errorMessage: action.messages && action.messages.failed || saveFailed
|
||||
})
|
||||
.then(async (response) => {
|
||||
response && onChange && onChange(store.data, difference(store.data, store.pristine));
|
||||
store.validated && this.validate(true);
|
||||
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
}
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
}
|
||||
|
||||
action.redirect && env.updateLocation(filter(action.redirect, store.data));
|
||||
action.reload && this.reloadTarget(action.reload, store.data);
|
||||
})
|
||||
.catch(() => { });
|
||||
action.redirect && env.updateLocation(filter(action.redirect, store.data));
|
||||
action.reload && this.reloadTarget(action.reload, store.data);
|
||||
})
|
||||
.catch(() => { });
|
||||
} else if (action.actionType === 'reload') {
|
||||
action.target && this.reloadTarget(action.target, data);
|
||||
} else if (onAction) {
|
||||
|
@ -7,8 +7,9 @@ import getExprProperties from '../utils/filter-schema';
|
||||
import {filter, evalExpression} from '../utils/tpl';
|
||||
import {createObject, mapTree, someTree} from '../utils/helper';
|
||||
import {resolveVariable} from '../utils/tpl-builtin';
|
||||
import {isApiOutdated} from '../utils/api';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||
import {Api} from '../types';
|
||||
|
||||
export interface Link {
|
||||
className?: string;
|
||||
@ -32,7 +33,7 @@ export interface NavigationProps extends RendererProps {
|
||||
className?: string;
|
||||
stacked?: boolean;
|
||||
links?: Links;
|
||||
source?: string;
|
||||
source?: Api;
|
||||
}
|
||||
|
||||
export default class Navigation extends React.Component<NavigationProps, NavigationState> {
|
||||
@ -50,6 +51,7 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||
links: this.syncLinks(
|
||||
props,
|
||||
(props.source &&
|
||||
typeof props.source === 'string' &&
|
||||
/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source) &&
|
||||
resolveVariable(props.source, props.data)) ||
|
||||
props.links
|
||||
@ -68,14 +70,14 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||
componentWillReceiveProps(nextProps: NavigationProps) {
|
||||
const props = this.props;
|
||||
|
||||
if (nextProps.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(nextProps.source)) {
|
||||
if (nextProps.source && /^\$(?:([a-z0-9_.]+)|{.+})$/.test(nextProps.source as string)) {
|
||||
if (nextProps.source !== props.source) {
|
||||
this.setState({
|
||||
links: this.syncLinks(nextProps),
|
||||
});
|
||||
} else {
|
||||
const links = resolveVariable(nextProps.source, nextProps.data);
|
||||
const prevLinks = resolveVariable(props.source, props.data);
|
||||
const links = resolveVariable(nextProps.source as string, nextProps.data);
|
||||
const prevLinks = resolveVariable(props.source as string, props.data);
|
||||
|
||||
if (links !== prevLinks) {
|
||||
this.setState({
|
||||
@ -97,7 +99,7 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||
componentDidUpdate(prevProps: NavigationProps) {
|
||||
const props = this.props;
|
||||
|
||||
if (props.source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source)) {
|
||||
if (props.source && !/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source as string)) {
|
||||
isApiOutdated(prevProps.source, props.source, prevProps.data, props.data) && this.reload();
|
||||
}
|
||||
}
|
||||
@ -114,7 +116,7 @@ export default class Navigation extends React.Component<NavigationProps, Navigat
|
||||
const {data, env, source} = this.props;
|
||||
const finalData = values ? createObject(data, values) : data;
|
||||
|
||||
if (!source || (source.sendOn && !evalExpression(source.sendOn, data))) {
|
||||
if (!source || !isEffectiveApi(source, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import qs from 'qs';
|
||||
import {isVisible, autobind, bulkBindFunctions} from '../utils/helper';
|
||||
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||
import Alert from '../components/Alert2';
|
||||
import {isApiOutdated} from '../utils/api';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
|
||||
export interface PageProps extends RendererProps {
|
||||
title?: string; // 标题
|
||||
@ -107,15 +107,11 @@ export default class Page extends React.Component<PageProps> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {initApi, initFetch, store, messages} = this.props;
|
||||
const {initApi, initFetch, initFetchOn, store, messages} = this.props;
|
||||
|
||||
this.mounted = true;
|
||||
|
||||
if (
|
||||
initApi &&
|
||||
initFetch &&
|
||||
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data))
|
||||
) {
|
||||
if (isEffectiveApi(initApi, store.data, initFetch, initFetchOn)) {
|
||||
store
|
||||
.fetchInitData(initApi, store.data, {
|
||||
successMessage: messages && messages.fetchSuccess,
|
||||
@ -153,7 +149,7 @@ export default class Page extends React.Component<PageProps> {
|
||||
(props.initFetch !== false && isApiOutdated(prevProps.initApi, initApi, prevProps.data, props.data))
|
||||
) {
|
||||
const messages = props.messages;
|
||||
(!(initApi as ApiObject).sendOn || evalExpression((initApi as ApiObject).sendOn as string, store.data)) &&
|
||||
isEffectiveApi(initApi, store.data) &&
|
||||
store
|
||||
.fetchData(initApi as Api, store.data, {
|
||||
successMessage: messages && messages.fetchSuccess,
|
||||
@ -189,20 +185,21 @@ export default class Page extends React.Component<PageProps> {
|
||||
} else if (action.actionType === 'drawer') {
|
||||
store.openDrawer(ctx);
|
||||
} else if (action.actionType === 'ajax') {
|
||||
store
|
||||
.saveRemote(action.api as string, ctx, {
|
||||
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
|
||||
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveSuccess),
|
||||
})
|
||||
.then(async () => {
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
}
|
||||
isEffectiveApi(action.api, ctx) &&
|
||||
store
|
||||
.saveRemote(action.api as string, ctx, {
|
||||
successMessage: (action.messages && action.messages.success) || (messages && messages.saveSuccess),
|
||||
errorMessage: (action.messages && action.messages.failed) || (messages && messages.saveSuccess),
|
||||
})
|
||||
.then(async () => {
|
||||
if (action.feedback && isVisible(action.feedback, store.data)) {
|
||||
await this.openFeedback(action.feedback, store.data);
|
||||
}
|
||||
|
||||
action.redirect && env.jumpTo(filter(action.redirect, store.data), action);
|
||||
action.reload && this.reloadTarget(action.reload, store.data);
|
||||
})
|
||||
.catch(() => {});
|
||||
action.redirect && env.jumpTo(filter(action.redirect, store.data), action);
|
||||
action.reload && this.reloadTarget(action.reload, store.data);
|
||||
})
|
||||
.catch(() => {});
|
||||
} else if (action.actionType === 'copy' && (action.content || action.copy)) {
|
||||
env.copy && env.copy(filter(action.content || action.copy, ctx));
|
||||
}
|
||||
@ -280,7 +277,7 @@ export default class Page extends React.Component<PageProps> {
|
||||
const {store, initApi} = this.props;
|
||||
|
||||
clearTimeout(this.timer);
|
||||
initApi &&
|
||||
isEffectiveApi(initApi, store.data) &&
|
||||
store
|
||||
.fetchData(initApi, store.data, {
|
||||
silent,
|
||||
|
@ -7,12 +7,13 @@ import {filter, evalExpression} from '../utils/tpl';
|
||||
import cx from 'classnames';
|
||||
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
|
||||
import {observer} from 'mobx-react';
|
||||
import {isApiOutdated} from '../utils/api';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
|
||||
export interface ServiceProps extends RendererProps {
|
||||
api?: Api;
|
||||
schemaApi?: Api;
|
||||
initFetch?: boolean;
|
||||
initFetchOn?: string;
|
||||
initFetchSchema?: boolean;
|
||||
interval?: number;
|
||||
silentPolling?: boolean;
|
||||
@ -51,6 +52,7 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
initFetchSchema,
|
||||
api,
|
||||
initFetch,
|
||||
initFetchOn,
|
||||
store,
|
||||
messages: {
|
||||
fetchSuccess,
|
||||
@ -60,22 +62,14 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
|
||||
this.mounted = true;
|
||||
|
||||
if (
|
||||
schemaApi &&
|
||||
initFetchSchema !== false &&
|
||||
(!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))
|
||||
) {
|
||||
if (isEffectiveApi(schemaApi, store.data, initFetchSchema)) {
|
||||
store.fetchSchema(schemaApi, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed
|
||||
}).then(this.initInterval);
|
||||
}
|
||||
|
||||
if (
|
||||
api &&
|
||||
initFetch !== false &&
|
||||
(!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, store.data))
|
||||
) {
|
||||
if (isEffectiveApi(api, store.data, initFetch, initFetchOn)) {
|
||||
store.fetchInitData(api, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed
|
||||
@ -94,7 +88,7 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
}
|
||||
} = props;
|
||||
|
||||
isApiOutdated(prevProps.api, props.api, prevProps.data, props.data) &&
|
||||
isApiOutdated(prevProps.api, props.api, prevProps.data, props.data)
|
||||
store.fetchData(props.api as Api, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed
|
||||
@ -102,9 +96,9 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
|
||||
isApiOutdated(prevProps.schemaApi, props.schemaApi, prevProps.data, props.data) &&
|
||||
store.fetchSchema(props.schemaApi as Api, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed
|
||||
}).then(this.initInterval);
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed
|
||||
}).then(this.initInterval);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -127,29 +121,21 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
return this.receive(query);
|
||||
}
|
||||
|
||||
const {schemaApi, fetchSchema, api, fetch, store, messages: {
|
||||
const {schemaApi, initFetchSchema, api, initFetch, initFetchOn, store, messages: {
|
||||
fetchSuccess,
|
||||
fetchFailed
|
||||
}} = this.props;
|
||||
|
||||
clearTimeout(this.timer);
|
||||
|
||||
if (
|
||||
schemaApi &&
|
||||
fetchSchema !== false &&
|
||||
(!(schemaApi as ApiObject).sendOn || evalExpression((schemaApi as ApiObject).sendOn as string, store.data))
|
||||
) {
|
||||
if (isEffectiveApi(schemaApi, store.data, initFetchSchema)) {
|
||||
store.fetchSchema(schemaApi, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed
|
||||
}).then(this.initInterval);
|
||||
}
|
||||
|
||||
if (
|
||||
api &&
|
||||
fetch !== false &&
|
||||
(!(api as ApiObject).sendOn || evalExpression((api as ApiObject).sendOn as string, store.data))
|
||||
) {
|
||||
if (isEffectiveApi(api, store.data, initFetch, initFetchOn)) {
|
||||
store
|
||||
.fetchData(api, store.data, {
|
||||
silent,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {Renderer, RendererProps} from '../factory';
|
||||
import {SchemaNode, Action, Schema, Api} from '../types';
|
||||
import {SchemaNode, Action, Schema, Api, ApiObject} from '../types';
|
||||
import forEach = require('lodash/forEach');
|
||||
import {filter} from '../utils/tpl';
|
||||
import cx from 'classnames';
|
||||
@ -12,6 +12,7 @@ import {TableStore, ITableStore, IColumn, IRow} from '../store/table';
|
||||
import {observer} from 'mobx-react';
|
||||
import {anyChanged, getScrollParent, difference, noop} from '../utils/helper';
|
||||
import {resolveVariable} from '../utils/tpl-builtin';
|
||||
import {isEffectiveApi} from '../utils/api';
|
||||
import debounce = require('lodash/debounce');
|
||||
import xor = require('lodash/xor');
|
||||
import QuickEdit from './QuickEdit';
|
||||
@ -1828,13 +1829,14 @@ export class HeadCellFilterDropDown extends React.Component<HeadCellFilterProps,
|
||||
}
|
||||
|
||||
fetchOptions() {
|
||||
const {env, filterable} = this.props;
|
||||
env.fetcher(filterable.source).then(ret => {
|
||||
let options = (ret.data && ret.data.options) || [];
|
||||
this.setState({
|
||||
filterOptions: ret && ret.data && this.alterOptions(options),
|
||||
const {env, filterable, data} = this.props;
|
||||
isEffectiveApi(filterable.source, data) &&
|
||||
env.fetcher(filterable.source).then(ret => {
|
||||
let options = (ret.data && ret.data.options) || [];
|
||||
this.setState({
|
||||
filterOptions: ret && ret.data && this.alterOptions(options),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
alterOptions(options: Array<any>) {
|
||||
|
@ -5,6 +5,7 @@ import cx from 'classnames';
|
||||
import getExprProperties from '../utils/filter-schema';
|
||||
import {Api, Payload} from '../types';
|
||||
import update = require('react-addons-update');
|
||||
import {isEffectiveApi} from '../utils/api';
|
||||
|
||||
export interface TaskProps extends RendererProps {
|
||||
className?: string;
|
||||
@ -107,11 +108,11 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (interval && !checkApi) {
|
||||
if (interval && !isEffectiveApi(checkApi)) {
|
||||
return alert('checkApi 没有设置, 不能及时获取任务状态');
|
||||
}
|
||||
|
||||
env &&
|
||||
isEffectiveApi(checkApi, data) && env &&
|
||||
env
|
||||
.fetcher(checkApi, data)
|
||||
.then(this.handleLoaded)
|
||||
@ -135,9 +136,9 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||
submitTask(item: TaskItem, index: number, retry = false) {
|
||||
const {submitApi, reSubmitApi, loadingStatusCode, errorStatusCode, data, env} = this.props;
|
||||
|
||||
if (!retry && !submitApi) {
|
||||
if (!retry && !isEffectiveApi(submitApi)) {
|
||||
return alert('submitApi 没有配置');
|
||||
} else if (retry && !reSubmitApi) {
|
||||
} else if (retry && !isEffectiveApi(reSubmitApi)) {
|
||||
return alert('reSubmitApi 没有配置');
|
||||
}
|
||||
|
||||
@ -158,9 +159,10 @@ export default class Task extends React.Component<TaskProps, TaskState> {
|
||||
} as any)
|
||||
);
|
||||
|
||||
env &&
|
||||
const api = retry ? reSubmitApi : submitApi;
|
||||
isEffectiveApi(api, data) && env &&
|
||||
env
|
||||
.fetcher(retry ? reSubmitApi : submitApi, {
|
||||
.fetcher(api, {
|
||||
...data,
|
||||
...item,
|
||||
})
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
|
||||
|
||||
import {Renderer, RendererProps} from '../factory';
|
||||
import {ServiceStore, IServiceStore} from '../store/service';
|
||||
import {Api, SchemaNode, Schema, Action} from '../types';
|
||||
@ -9,7 +8,7 @@ import {filter, evalExpression} from '../utils/tpl';
|
||||
import cx = require('classnames');
|
||||
import {observer} from 'mobx-react';
|
||||
import {createObject, until, isVisible} from '../utils/helper';
|
||||
import {buildApi, isValidApi, isApiOutdated} from '../utils/api';
|
||||
import {isApiOutdated, isEffectiveApi} from '../utils/api';
|
||||
import {IFormStore} from '../store/form';
|
||||
|
||||
export type TabProps = Schema & {
|
||||
@ -92,13 +91,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
onInit,
|
||||
} = this.props;
|
||||
|
||||
if (initApi && initFetch !== false && (!initApi.sendOn || evalExpression(initApi.sendOn, data))) {
|
||||
if (isEffectiveApi(initApi, store.data, initFetch)) {
|
||||
store
|
||||
.fetchInitData(initApi, store.data, {
|
||||
successMessage: fetchSuccess,
|
||||
errorMessage: fetchFailed,
|
||||
onSuccess: () => {
|
||||
if (!initAsyncApi || store.data[initFinishedField || 'finished']) {
|
||||
if (!isEffectiveApi(initAsyncApi, store.data) || store.data[initFinishedField || 'finished']) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -202,7 +201,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
const step = steps[this.state.currentStep - 1];
|
||||
let finnalAsyncApi = step && step.asyncApi || this.state.currentStep === steps.length && asyncApi;
|
||||
|
||||
if (!step || !finnalAsyncApi) {
|
||||
if (!step || !isEffectiveApi(finnalAsyncApi, store.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -306,16 +305,16 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
if (this.state.currentStep < steps.length) {
|
||||
let finnalAsyncApi = action.asyncApi || step.asyncApi;
|
||||
|
||||
finnalAsyncApi &&
|
||||
isEffectiveApi(finnalAsyncApi, store.data) &&
|
||||
store.updateData({
|
||||
[finishedField || 'finished']: false,
|
||||
});
|
||||
|
||||
if (step.api || action.api) {
|
||||
if (isEffectiveApi(step.api || action.api, store.data)) {
|
||||
store
|
||||
.saveRemote(action.api || step.api, store.data, {
|
||||
onSuccess: () => {
|
||||
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
||||
if (!isEffectiveApi(finnalAsyncApi, store.data) || store.data[finishedField || 'finished']) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -340,7 +339,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
} else if (action.api || step.api || api) {
|
||||
let finnalAsyncApi = action.asyncApi || step.asyncApi || asyncApi;
|
||||
|
||||
finnalAsyncApi &&
|
||||
isEffectiveApi(finnalAsyncApi, store.data) &&
|
||||
store.updateData({
|
||||
[finishedField || 'finished']: false,
|
||||
});
|
||||
@ -348,43 +347,44 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
const formStore = this.form ? (this.form.props.store as IFormStore) : store;
|
||||
store.markSaving(true);
|
||||
|
||||
formStore
|
||||
.saveRemote(action.api || step.api || api, store.data, {
|
||||
onSuccess: () => {
|
||||
if (!finnalAsyncApi || store.data[finishedField || 'finished']) {
|
||||
return;
|
||||
isEffectiveApi(action.api || step.api || api, store.data) &&
|
||||
formStore
|
||||
.saveRemote(action.api || step.api || api, store.data, {
|
||||
onSuccess: () => {
|
||||
if (!isEffectiveApi(finnalAsyncApi, store.data) || store.data[finishedField || 'finished']) {
|
||||
return;
|
||||
}
|
||||
|
||||
return until(
|
||||
() => store.checkRemote(finnalAsyncApi as Api, store.data),
|
||||
(ret: any) => ret && ret[finishedField || 'finished'],
|
||||
cancel => (this.asyncCancel = cancel)
|
||||
);
|
||||
},
|
||||
})
|
||||
.then(value => {
|
||||
store.updateData({
|
||||
...store.data,
|
||||
...value
|
||||
});
|
||||
store.markSaving(false);
|
||||
if (onFinished && onFinished(value, action) === false) {
|
||||
// 如果是 false 后面的操作就不执行
|
||||
return value;
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
env.updateLocation(filter(redirect, store.data));
|
||||
} else if (reload) {
|
||||
this.reloadTarget(reload, store.data);
|
||||
}
|
||||
|
||||
return until(
|
||||
() => store.checkRemote(finnalAsyncApi as Api, store.data),
|
||||
(ret: any) => ret && ret[finishedField || 'finished'],
|
||||
cancel => (this.asyncCancel = cancel)
|
||||
);
|
||||
},
|
||||
})
|
||||
.then(value => {
|
||||
store.updateData({
|
||||
...store.data,
|
||||
...value
|
||||
});
|
||||
store.markSaving(false);
|
||||
if (onFinished && onFinished(value, action) === false) {
|
||||
// 如果是 false 后面的操作就不执行
|
||||
return value;
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
env.updateLocation(filter(redirect, store.data));
|
||||
} else if (reload) {
|
||||
this.reloadTarget(reload, store.data);
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
.catch(e => {
|
||||
store.markSaving(false);
|
||||
console.error(e);
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
store.markSaving(false);
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { ApiObject } from './../types';
|
||||
import {
|
||||
types,
|
||||
getParent,
|
||||
@ -14,6 +13,7 @@ import {
|
||||
} from './index';
|
||||
import {
|
||||
Api,
|
||||
ApiObject,
|
||||
Payload,
|
||||
fetchOptions
|
||||
} from '../types';
|
||||
|
@ -15,6 +15,7 @@ const rSchema = /(?:^|raw\:)(get|post|put|delete|patch):/i;
|
||||
import qs from 'qs';
|
||||
import { evalExpression } from './tpl';
|
||||
import {
|
||||
isObject,
|
||||
isObjectShallowModified
|
||||
} from './helper';
|
||||
|
||||
@ -156,6 +157,27 @@ export function isValidApi(api: string) {
|
||||
return api && /^(?:https?:\/\/[^\/]+)?(\/[^\s\/\?]*){1,}(\?.*)?$/.test(api);
|
||||
}
|
||||
|
||||
export function isEffectiveApi(api?: Api, data?: any, initFetch?: boolean, initFetchOn?: string) {
|
||||
if (!api) {
|
||||
return false;
|
||||
}
|
||||
if (initFetch === false) {
|
||||
return false;
|
||||
}
|
||||
if (initFetchOn && data && !evalExpression(initFetchOn, data)) {
|
||||
return false;
|
||||
}
|
||||
if (typeof api === 'string' && isValidApi(api)) {
|
||||
return true;
|
||||
} else if (isObject(api) && isValidApi((api as ApiObject).url)) {
|
||||
if ((api as ApiObject).sendOn && data && !evalExpression((api as ApiObject).sendOn as string, data)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isSameApi(apiA: ApiObject | ApiCacheConfig, apiB: ApiObject | ApiCacheConfig): boolean {
|
||||
return apiA.method === apiB.method && apiA.url === apiB.url && !isObjectShallowModified(apiA.data, apiB.data, false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user