image、file支持autoFill;支持操作结束后关闭指定dialog

This commit is contained in:
rickcole 2020-06-17 16:16:01 +08:00
parent 6dfa4890ea
commit 52c30c75b7
16 changed files with 145 additions and 38 deletions

View File

@ -21,26 +21,26 @@ Action 是一种特殊的渲染器,它本身是一个按钮,同时它能触
所有`actionType`都支持的通用配置项
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | --------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | `string` | `action` | 指定为 Action 渲染器,也可以是 `button`、`submit`、`reset`。 |
| actionType | `string` | - | 【必填】这是 action 最核心的配置,来指定该 action 的作用类型,支持:`ajax`、`link`、`url`、`drawer`、`dialog`、`confirm`、`cancel`、`prev`、`next`、`copy`、`close`。 |
| label | `string` | - | 按钮文本。可用 `${xxx}` 取值。 |
| level | `string` | `default` | 按钮样式,支持:`link`、`primary`、`secondary`、`info`、`success`、`warning`、`danger`、`light`、`dark`、`default`。 |
| size | `string` | - | 按钮大小,支持:`xs`、`sm`、`md`、`lg`。 |
| icon | `string` | - | 设置图标,例如`fa fa-plus`。 |
| iconClassName | `string` | - | 给图标上添加类名。 |
| active | `boolean` | - | 按钮是否高亮。 |
| activeLevel | `string` | - | 按钮高亮时的样式,配置支持同`level`。 |
| activeClassName | `string` | `is-active` | 给按钮高亮添加类名。 |
| block | `boolean` | - | 用`display:"block"`来显示按钮。 |
| confirmText | `string` | - | 当设置后,操作在开始前会询问用户。可用 `${xxx}` 取值。 |
| reload | `string` | - | 指定此次操作完后,需要刷新的目标组件名字(组件的`name`值,自己配置的),多个请用 `,` 号隔开。 |
| tooltip | `string` | - | 鼠标停留时弹出该段文字,也可以配置对象类型:字段为`title`和`content`。可用 `${xxx}` 取值。 |
| disabledTip | `string` | - | 被禁用后鼠标停留时弹出该段文字,也可以配置对象类型:字段为`title`和`content`。可用 `${xxx}` 取值。 |
| tooltipPlacement | `string` | `top` | 如果配置了`tooltip`或者`disabledTip`,指定提示信息位置,可配置`top`、`bottom`、`left`、`right`。 |
| close | `boolean` | - | 当`action`配置在`dialog`或`drawer`的`actions`中时,配置为`true`指定此次操作完后关闭当前`dialog`或`drawer`。 |
| required | `Array<string>` | - | 配置字符串数组,指定在`form`中进行操作之前,需要指定的字段名的表单项通过验证 |
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | ------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | `string` | `action` | 指定为 Action 渲染器,也可以是 `button`、`submit`、`reset`。 |
| actionType | `string` | - | 【必填】这是 action 最核心的配置,来指定该 action 的作用类型,支持:`ajax`、`link`、`url`、`drawer`、`dialog`、`confirm`、`cancel`、`prev`、`next`、`copy`、`close`。 |
| label | `string` | - | 按钮文本。可用 `${xxx}` 取值。 |
| level | `string` | `default` | 按钮样式,支持:`link`、`primary`、`secondary`、`info`、`success`、`warning`、`danger`、`light`、`dark`、`default`。 |
| size | `string` | - | 按钮大小,支持:`xs`、`sm`、`md`、`lg`。 |
| icon | `string` | - | 设置图标,例如`fa fa-plus`。 |
| iconClassName | `string` | - | 给图标上添加类名。 |
| active | `boolean` | - | 按钮是否高亮。 |
| activeLevel | `string` | - | 按钮高亮时的样式,配置支持同`level`。 |
| activeClassName | `string` | `is-active` | 给按钮高亮添加类名。 |
| block | `boolean` | - | 用`display:"block"`来显示按钮。 |
| confirmText | `string` | - | 当设置后,操作在开始前会询问用户。可用 `${xxx}` 取值。 |
| reload | `string` | - | 指定此次操作完后,需要刷新的目标组件名字(组件的`name`值,自己配置的),多个请用 `,` 号隔开。 |
| tooltip | `string` | - | 鼠标停留时弹出该段文字,也可以配置对象类型:字段为`title`和`content`。可用 `${xxx}` 取值。 |
| disabledTip | `string` | - | 被禁用后鼠标停留时弹出该段文字,也可以配置对象类型:字段为`title`和`content`。可用 `${xxx}` 取值。 |
| tooltipPlacement | `string` | `top` | 如果配置了`tooltip`或者`disabledTip`,指定提示信息位置,可配置`top`、`bottom`、`left`、`right`。 |
| close | `boolean`或`string` | - | 当`action`配置在`dialog`或`drawer`的`actions`中时,配置为`true`指定此次操作完后关闭当前`dialog`或`drawer`。 也可以配置字符串,指定此次操作完后需要关闭的`dialog`或者`drawer`的`name`值(在弹框或抽屉上配置 name 属性),多个请用`,`号隔开 |
| required | `Array<string>` | - | 配置字符串数组,指定在`form`中进行操作之前,需要指定的字段名的表单项通过验证 |
下面会分别介绍每种类型的 Action 配置项

View File

@ -74,7 +74,7 @@ button-group 有两种模式,除了能让按钮组合在一起,还能做类
- `size` 按钮大小,从小到大依次为`xs, sm, md, lg`
- `disabled` 是否禁用`options` 中选项
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address`
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
```schema:height="250" scope="form"

View File

@ -22,6 +22,8 @@
- `startChunkApi` 默认 `/api/upload/startChunk` 想自己存储时才需要关注。
- `chunkApi` 默认 `/api/upload/chunk` 想自己存储时才需要关注。
- `finishChunkApi` 默认 `/api/upload/finishChunk` 想自己存储时才需要关注。
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项中
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
```schema:height="250" scope="form-item"

View File

@ -21,6 +21,8 @@
- `maxWidth` 限制图片最大宽度。
- `maxHeight` 限制图片最大高度。
- `aspectRatio` 限制图片宽高比,格式为浮点型数字,默认 `1``1:1`,如果要设置 `16:9` 请设置 `1.7777777777777777``16 / 9`。 如果不想限制比率,请设置空字符串。
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项中
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
```schema:height="250" scope="form-item"

View File

@ -15,7 +15,7 @@
- `delimiter` 默认为 `,`
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 value 的值封装为数组,作为当前表单项的值。
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address`
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
单选

View File

@ -16,7 +16,7 @@
- `modalMode` 设置 `dialog` 或者 `drawer`,用来配置弹出方式。
- `pickerSchema` 默认为 `{mode: 'list', listItem: {title: '${label}'}}`, 即用 List 类型的渲染,来展示列表信息。更多的玩法请参考 [CRUD](../CRUD.md) 的配置。
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address`
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
```schema:height="300" scope="form-item"

View File

@ -9,7 +9,7 @@
- `source` Api 地址,如果选项不固定,可以通过配置 `source` 动态拉取。
- `columnsCount` 默认为 `1` 可以配置成一行显示多个。
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中。
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address`
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
```schema:height="330" scope="form"

View File

@ -21,7 +21,7 @@
- `checkAllLabel` 默认为 `全选`, 全选的文字
- `defaultCheckAll` 是否默认全选,默认为`false`
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address`
- 配置`"autoFill": {"address": "${label}"}`,表示将选中项中的`label`的值,自动填充到当前`name`为`address`的表单项
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
单选

View File

@ -36,6 +36,7 @@ export interface IScopedContext {
getComponents: () => Array<ScopedComponentType>;
reload: (target: string, ctx: RendererData) => void;
send: (target: string, ctx: RendererData) => void;
close: (target: string) => void;
}
type AlisIScopedContext = IScopedContext;
export const ScopedContext = React.createContext(createScopedTools(''));
@ -159,6 +160,22 @@ function createScopedTools(
env.updateLocation(link);
}
});
},
/**
*
*
* @param target name
*/
close(target: string) {
const scoped = this;
let targets =
typeof target === 'string' ? target.split(/\s*,\s*/) : target;
targets.forEach(name => {
const component = scoped.getComponentByName(name);
component && component.props.onClose && component.props.onClose();
});
}
};
}
@ -182,8 +199,9 @@ export function HocScoped<
scopeRef?: (ref: any) => void;
}
> {
static displayName = `Scoped(${ComposedComponent.displayName ||
ComposedComponent.name})`;
static displayName = `Scoped(${
ComposedComponent.displayName || ComposedComponent.name
})`;
static contextType = ScopedContext;
static ComposedComponent = ComposedComponent;
ref: any;
@ -218,7 +236,12 @@ export function HocScoped<
return (
<ScopedContext.Provider value={this.scoped}>
<ComposedComponent {...rest as any /* todo */} ref={this.childRef} />
<ComposedComponent
{
...(rest as any) /* todo */
}
ref={this.childRef}
/>
</ScopedContext.Provider>
);
}

View File

@ -318,7 +318,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
pickerMode,
env,
pageField,
stopAutoRefreshWhenModalIsOpen
stopAutoRefreshWhenModalIsOpen,
onClose
} = this.props;
if (action.actionType === 'dialog') {
@ -363,6 +364,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
action.reload
? this.reloadTarget(action.reload, data)
: this.search(undefined, undefined, true, true);
if (action.close) {
onClose();
this.closeTarget(action.close);
}
})
.catch(() => {});
} else if (
@ -391,7 +396,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
messages,
pageField,
stopAutoRefreshWhenModalIsOpen,
env
env,
onClose
} = this.props;
if (!selectedItems.length && action.requireSelected !== false) {
@ -443,6 +449,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
action.reload
? this.reloadTarget(action.reload, data)
: this.search({[pageField || 'page']: 1}, undefined, true);
if (action.close) {
onClose();
this.closeTarget(action.close);
}
const redirect = action.redirect && filter(action.redirect, data);
redirect && env.jumpTo(redirect, action);
@ -1143,6 +1153,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
// implement this.
}
closeTarget(target: string) {
// implement this.
}
doAction(action: Action, data: object, throwErrors: boolean = false) {
return this.handleAction(undefined, action, data, throwErrors);
}
@ -1779,4 +1793,9 @@ export class CRUDRenderer extends CRUD {
const scoped = this.context as IScopedContext;
scoped.reload(target, data);
}
closeTarget(target: string) {
const scoped = this.context as IScopedContext;
scoped.close(target);
}
}

View File

@ -305,6 +305,7 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
key,
disabled: (body && (body as any).disabled) || store.loading,
onAction: this.handleAction,
onClose: this.handleSelfClose,
onFinished: this.handleChildFinished,
affixOffsetTop: 0,
onChange: this.handleFormChange,
@ -595,7 +596,9 @@ export class DialogRenderer extends Dialog {
) {
onConfirm && onConfirm(values, rawAction || action, ctx, targets);
} else if (action.close) {
this.handleSelfClose();
action.close === true
? this.handleSelfClose()
: this.closeTarget(action.close);
}
store.markBusying(false);
})
@ -639,6 +642,7 @@ export class DialogRenderer extends Dialog {
) {
store.setCurrentAction(action);
this.handleSelfClose();
action.close && this.closeTarget(action.close);
} else if (action.actionType === 'confirm') {
store.setCurrentAction(action);
this.tryChildrenToHandle(
@ -691,7 +695,10 @@ export class DialogRenderer extends Dialog {
action.redirect && filter(action.redirect, store.data);
reidrect && env.jumpTo(reidrect, action);
action.reload && this.reloadTarget(action.reload, store.data);
action.close && this.handleSelfClose();
if (action.close) {
this.handleSelfClose();
this.closeTarget(action.close);
}
})
.catch(() => {});
} else if (onAction) {
@ -779,4 +786,9 @@ export class DialogRenderer extends Dialog {
const scoped = this.context as IScopedContext;
scoped.reload(target, data);
}
closeTarget(target: string) {
const scoped = this.context as IScopedContext;
scoped.close(target);
}
}

View File

@ -275,6 +275,7 @@ export default class Drawer extends React.Component<DrawerProps, object> {
key,
disabled: store.loading,
onAction: this.handleAction,
onClose: this.handleSelfClose,
onFinished: this.handleChildFinished,
popOverContainer: this.getPopOverContainer,
onChange: this.handleFormChange,
@ -621,7 +622,9 @@ export class DrawerRenderer extends Drawer {
) {
onConfirm && onConfirm(values, rawAction || action, ctx, targets);
} else if (action.close) {
this.handleSelfClose();
action.close === true
? this.handleSelfClose()
: this.closeTarget(action.close);
}
store.markBusying(false);
})
@ -656,6 +659,7 @@ export class DrawerRenderer extends Drawer {
if (action.actionType === 'close' || action.actionType === 'cancel') {
store.setCurrentAction(action);
onClose();
action.close && this.closeTarget(action.close);
} else if (action.actionType === 'confirm') {
store.setCurrentAction(action);
this.tryChildrenToHandle(action, data) || onClose();
@ -686,7 +690,10 @@ export class DrawerRenderer extends Drawer {
action.redirect && filter(action.redirect, store.data);
redirect && env.jumpTo(redirect, action);
action.reload && this.reloadTarget(action.reload, store.data);
action.close && this.handleSelfClose();
if (action.close) {
this.handleSelfClose();
this.closeTarget(action.close);
}
})
.catch(() => {});
} else if (onAction) {
@ -769,4 +776,9 @@ export class DrawerRenderer extends Drawer {
const scoped = this.context as IScopedContext;
scoped.reload(target, data);
}
closeTarget(target: string) {
const scoped = this.context as IScopedContext;
scoped.close(target);
}
}

View File

@ -10,11 +10,12 @@ import ImageControl from './Image';
import {Payload, ApiObject, ApiString} from '../../types';
import {filter} from '../../utils/tpl';
import Alert from '../../components/Alert2';
import {qsstringify, createObject, guid} from '../../utils/helper';
import {qsstringify, createObject, guid, isEmpty} from '../../utils/helper';
import {buildApi} from '../../utils/api';
import Button from '../../components/Button';
import {Icon} from '../../components/icons';
import DropZone from 'react-dropzone';
import {dataMapping} from '../../utils/tpl-builtin';
export interface FileProps extends FormControlProps {
maxSize: number;
@ -47,6 +48,7 @@ export interface FileProps extends FormControlProps {
asBase64?: boolean;
asBlob?: boolean;
resetValue?: string;
autoFill?: Object;
}
export interface FileX extends File {
@ -496,7 +498,10 @@ export default class FileControl extends React.Component<FileProps, FileState> {
asBase64,
asBlob,
data,
translate: __
translate: __,
multiple,
autoFill,
onBulkChange
} = this.props;
if (asBase64) {
@ -558,6 +563,13 @@ export default class FileControl extends React.Component<FileProps, FileState> {
onProgress(1);
const value = (ret.data as any).value || ret.data;
const sendTo =
!multiple &&
autoFill &&
!isEmpty(autoFill) &&
dataMapping(autoFill, ret.data);
sendTo && onBulkChange(sendTo);
cb(null, file, {
...(isPlainObject(ret.data) ? ret.data : null),
value: value,

View File

@ -8,7 +8,7 @@ import find from 'lodash/find';
import qs from 'qs';
import {Payload} from '../../types';
import {buildApi} from '../../utils/api';
import {createObject, qsstringify, guid} from '../../utils/helper';
import {createObject, qsstringify, guid, isEmpty} from '../../utils/helper';
import {Icon} from '../../components/icons';
import Button from '../../components/Button';
// @ts-ignore
@ -16,6 +16,7 @@ import accepts from 'attr-accept';
import {getNameFromUrl} from './File';
import ImageComponent, {ImageThumbProps} from '../Image';
import {TranslateFn} from '../../locale';
import {dataMapping} from '../../utils/tpl-builtin';
let preventEvent = (e: any) => e.stopPropagation();
@ -57,6 +58,7 @@ export interface ImageProps extends FormControlProps {
>;
}
) => void;
autoFill?: Object;
}
export interface ImageState {
@ -762,7 +764,7 @@ export default class ImageControl extends React.Component<
cb: (error: null | string, file: Blob, obj?: FileValue) => void,
onProgress: (progress: number) => void
) {
const __ = this.props.translate;
const {translate: __, multiple, autoFill, onBulkChange} = this.props;
this._send(file, this.props.reciever as string, {}, onProgress)
.then((ret: Payload) => {
if (ret.status) {
@ -775,6 +777,13 @@ export default class ImageControl extends React.Component<
};
obj.value = obj.value || obj.url;
const sendTo =
!multiple &&
autoFill &&
!isEmpty(autoFill) &&
dataMapping(autoFill, obj);
sendTo && onBulkChange(sendTo);
cb(null, file, obj);
})
.catch(error => cb(error.message || __('上传失败,请重试'), file));

View File

@ -75,6 +75,7 @@ export interface OptionsControlProps extends FormControlProps, OptionProps {
onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
removable?: boolean;
onDelete?: (value: Option) => void;
autoFill?: Object;
}
// 自己接收的属性。

View File

@ -558,6 +558,7 @@ export default class Form extends React.Component<FormProps, object> {
onReset,
onFinished,
onFailed,
onClose,
redirect,
reload,
target,
@ -688,6 +689,7 @@ export default class Form extends React.Component<FormProps, object> {
this.reloadTarget(action.reload || reload, store.data);
}
action.close && this.closeTarget(action.close);
return values;
})
.catch(reason => {
@ -746,6 +748,10 @@ export default class Form extends React.Component<FormProps, object> {
redirect && env.jumpTo(redirect, action);
action.reload && this.reloadTarget(action.reload, store.data);
if (action.close) {
onClose();
this.closeTarget(action.close);
}
})
.catch(() => {});
} else if (action.actionType === 'reload') {
@ -827,6 +833,10 @@ export default class Form extends React.Component<FormProps, object> {
// 会被覆写
}
closeTarget(target: string) {
// 会被覆写
}
openFeedback(dialog: any, ctx: any) {
return new Promise(resolve => {
const {store} = this.props;
@ -1277,6 +1287,11 @@ export class FormRenderer extends Form {
scoped.reload(target, data);
}
closeTarget(target: string) {
const scoped = this.context as IScopedContext;
scoped.close(target);
}
reload(target?: string, query?: any, ctx?: any, silent?: boolean) {
if (query) {
return this.receive(query);