Merge pull request #181 from catchonme/master
form增加trim,treeSelect增加maxLength/minLength
This commit is contained in:
commit
1702089df6
|
@ -222,7 +222,7 @@ class MyComponent extends React.Component<any, any> {
|
||||||
|
|
||||||
注意:以上的 SDK 地址是一个页面跳转,会跳转到一个 CDN 地址,而且每次跳转都是最新的版本,随着 amis 的升级这个地址会一直变动,如果你的页面已经完成功能回归,请直接使用某个固定地址,这样才不会因为 amis 升级而导致你的页面不可用。
|
注意:以上的 SDK 地址是一个页面跳转,会跳转到一个 CDN 地址,而且每次跳转都是最新的版本,随着 amis 的升级这个地址会一直变动,如果你的页面已经完成功能回归,请直接使用某个固定地址,这样才不会因为 amis 升级而导致你的页面不可用。
|
||||||
|
|
||||||
另外,sdk 代码也伴随 npm 一起发布了,不实用 CDN 版本,直接替换成npm包里面的 `amis/sdk/sdk.js` 和 `amis/sdk/sdk.css` 即可。
|
另外,sdk 代码也伴随 npm 一起发布了,不使用 CDN 版本,直接替换成npm包里面的 `amis/sdk/sdk.js` 和 `amis/sdk/sdk.css` 即可。
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ amis 页面是通过 JSON 配置出来的,是由一个一个渲染模型组成
|
||||||
- [Date-Range](./renderers/Form/Date-Range.md): 日期范围类型
|
- [Date-Range](./renderers/Form/Date-Range.md): 日期范围类型
|
||||||
- [Color](./renderers/Form/Color.md): 颜色选择器
|
- [Color](./renderers/Form/Color.md): 颜色选择器
|
||||||
- [Range](./renderers/Form/Range.md): 范围输入框
|
- [Range](./renderers/Form/Range.md): 范围输入框
|
||||||
- [Image](./renderers/Form/Image.md): 图片格式输
|
- [Image](./renderers/Form/Image.md): 图片输入
|
||||||
- [File](./renderers/Form/File.md): 文件输入
|
- [File](./renderers/Form/File.md): 文件输入
|
||||||
- [Matrix](./renderers/Form/Matrix.md): 矩阵类型的输入框
|
- [Matrix](./renderers/Form/Matrix.md): 矩阵类型的输入框
|
||||||
- [Tree](./renderers/Form/Tree.md): 树形结构输入框
|
- [Tree](./renderers/Form/Tree.md): 树形结构输入框
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
## Each
|
||||||
|
|
||||||
|
基于现有变量循环输出渲染器
|
||||||
|
|
||||||
|
- `type` 请设置 `each`。
|
||||||
|
- `value` 格式为数组。
|
||||||
|
- `items` 使用`value`中的数据,循环输出渲染器。
|
||||||
|
|
||||||
|
|
||||||
|
```schema:height="160" scope="body"
|
||||||
|
{
|
||||||
|
"type": "each",
|
||||||
|
"value": ["A", "B", "C"],
|
||||||
|
"items": {
|
||||||
|
"type": "tpl",
|
||||||
|
"tpl": "<span class='label label-default'><%= data.item %></span> "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -25,6 +25,7 @@
|
||||||
- `minHeight` 限制图片最小高度。
|
- `minHeight` 限制图片最小高度。
|
||||||
- `maxWidth` 限制图片最大宽度。
|
- `maxWidth` 限制图片最大宽度。
|
||||||
- `maxHeight` 限制图片最大高度。
|
- `maxHeight` 限制图片最大高度。
|
||||||
|
- `aspectRatio` 限制图片宽高比,格式为浮点型数字,默认 `1` 即 `1:1`,如果要设置 `16:9` 请设置 `1.7777777777777777` 即 `16 / 9`。。
|
||||||
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
|
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
|
||||||
|
|
||||||
```schema:height="250" scope="form-item"
|
```schema:height="250" scope="form-item"
|
||||||
|
|
|
@ -436,6 +436,13 @@ export default {
|
||||||
cb(null, makeMarkdownRenderer(doc));
|
cb(null, makeMarkdownRenderer(doc));
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Each',
|
||||||
|
path: '/docs/renderers/Each',
|
||||||
|
getComponent: (location, cb) => require(['../../docs/renderers/Each.md'], (doc) => {
|
||||||
|
cb(null, makeMarkdownRenderer(doc));
|
||||||
|
}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Tpl',
|
label: 'Tpl',
|
||||||
path: '/docs/renderers/Tpl',
|
path: '/docs/renderers/Tpl',
|
||||||
|
|
|
@ -51,6 +51,8 @@ interface TreeSelectorProps {
|
||||||
rootValue?: any;
|
rootValue?: any;
|
||||||
cascade?: boolean;
|
cascade?: boolean;
|
||||||
selfDisabledAffectChildren?: boolean;
|
selfDisabledAffectChildren?: boolean;
|
||||||
|
minLength?: number;
|
||||||
|
maxLength?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TreeSelectorState {
|
interface TreeSelectorState {
|
||||||
|
@ -292,6 +294,8 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
highlightTxt,
|
highlightTxt,
|
||||||
data,
|
data,
|
||||||
|
maxLength,
|
||||||
|
minLength
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let childrenChecked = 0;
|
let childrenChecked = 0;
|
||||||
|
@ -327,6 +331,16 @@ export class TreeSelector extends React.Component<TreeSelectorProps, TreeSelecto
|
||||||
|
|
||||||
let nodeDisabled = !!uncheckable || !!disabled || selfDisabled;
|
let nodeDisabled = !!uncheckable || !!disabled || selfDisabled;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!nodeDisabled
|
||||||
|
&& (
|
||||||
|
(maxLength && !selfChecked && this.state.value.length >= maxLength)
|
||||||
|
|| (minLength && selfChecked && this.state.value.length <= minLength)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
nodeDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
const checkbox: JSX.Element | null = multiple ? (
|
const checkbox: JSX.Element | null = multiple ? (
|
||||||
<label className={cx(`Checkbox Checkbox--checkbox Checkbox--sm`)}>
|
<label className={cx(`Checkbox Checkbox--checkbox Checkbox--sm`)}>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -226,6 +226,10 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWrappedInstance() {
|
||||||
|
return this.input;
|
||||||
|
}
|
||||||
|
|
||||||
inputRef(ref:any) {
|
inputRef(ref:any) {
|
||||||
this.input = ref;
|
this.input = ref;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,22 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate():any {
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
minLength,
|
||||||
|
maxLength,
|
||||||
|
delimiter
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let curValue = Array.isArray(value) ? value : (value ? value : '').split(delimiter || ',');
|
||||||
|
if (minLength && curValue.length < minLength) {
|
||||||
|
return `已选择数量低于设定的最小个数${minLength},请选择更多的选项。`;
|
||||||
|
} else if (maxLength && curValue.length > maxLength) {
|
||||||
|
return `已选择数量超出设定的最大个数${maxLength},请取消选择超出的选项。`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeItem(index:number, e?: React.MouseEvent<HTMLElement>) {
|
removeItem(index:number, e?: React.MouseEvent<HTMLElement>) {
|
||||||
const {
|
const {
|
||||||
selectedOptions,
|
selectedOptions,
|
||||||
|
@ -344,7 +360,9 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||||
classPrefix: ns,
|
classPrefix: ns,
|
||||||
optionsPlaceholder,
|
optionsPlaceholder,
|
||||||
searchable,
|
searchable,
|
||||||
autoComplete
|
autoComplete,
|
||||||
|
maxLength,
|
||||||
|
minLength
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let filtedOptions = !isEffectiveApi(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;
|
||||||
|
@ -389,6 +407,8 @@ export default class TreeSelectControl extends React.Component<TreeSelectProps,
|
||||||
hideRoot
|
hideRoot
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
nameField="label"
|
nameField="label"
|
||||||
|
maxLength={maxLength}
|
||||||
|
minLength={minLength}
|
||||||
/>
|
/>
|
||||||
</PopOver>
|
</PopOver>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -89,6 +89,7 @@ export interface FormProps extends RendererProps, FormSchema {
|
||||||
canAccessSuperData: boolean;
|
canAccessSuperData: boolean;
|
||||||
persistData: boolean; // 开启本地缓存
|
persistData: boolean; // 开启本地缓存
|
||||||
clearPersistDataAfterSubmit: boolean; // 提交成功后清空本地缓存
|
clearPersistDataAfterSubmit: boolean; // 提交成功后清空本地缓存
|
||||||
|
trimValues?: boolean;
|
||||||
onInit?: (values:object) => any;
|
onInit?: (values:object) => any;
|
||||||
onReset?: (values:object) => void;
|
onReset?: (values:object) => void;
|
||||||
onSubmit?: (values:object, action:any) => any;
|
onSubmit?: (values:object, action:any) => any;
|
||||||
|
@ -486,9 +487,14 @@ export default class Form extends React.Component<FormProps, object> {
|
||||||
target,
|
target,
|
||||||
env,
|
env,
|
||||||
onChange,
|
onChange,
|
||||||
clearPersistDataAfterSubmit
|
clearPersistDataAfterSubmit,
|
||||||
|
trimValues
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
if (trimValues) {
|
||||||
|
store.trimValues();
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(action.required) && action.required.length) {
|
if (Array.isArray(action.required) && action.required.length) {
|
||||||
return store
|
return store
|
||||||
.validateFields(action.required)
|
.validateFields(action.required)
|
||||||
|
@ -1029,7 +1035,7 @@ export class FormRenderer extends Form {
|
||||||
}
|
}
|
||||||
|
|
||||||
const component = scoped.getComponentByName(name);
|
const component = scoped.getComponentByName(name);
|
||||||
component && component.receive && component && component.receive(values, subPath);
|
component && component.receive && component.receive(values, subPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,8 @@ import {
|
||||||
difference,
|
difference,
|
||||||
guid,
|
guid,
|
||||||
isObject,
|
isObject,
|
||||||
isEmpty
|
isEmpty,
|
||||||
|
mapObject
|
||||||
} from '../utils/helper';
|
} from '../utils/helper';
|
||||||
import { IComboStore } from "./combo";
|
import { IComboStore } from "./combo";
|
||||||
import isEqual = require('lodash/isEqual');
|
import isEqual = require('lodash/isEqual');
|
||||||
|
@ -180,6 +181,11 @@ export const FormStore = ServiceStore
|
||||||
self.data = data;
|
self.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trimValues() {
|
||||||
|
let data = mapObject(self.data, (item:any) => typeof item === 'string' ? item.trim() : item);
|
||||||
|
self.updateData(data);
|
||||||
|
}
|
||||||
|
|
||||||
function syncOptions() {
|
function syncOptions() {
|
||||||
self.items.forEach(item => item.syncOptions());
|
self.items.forEach(item => item.syncOptions());
|
||||||
}
|
}
|
||||||
|
@ -410,6 +416,7 @@ export const FormStore = ServiceStore
|
||||||
setInited,
|
setInited,
|
||||||
setValues,
|
setValues,
|
||||||
setValueByName,
|
setValueByName,
|
||||||
|
trimValues,
|
||||||
submit,
|
submit,
|
||||||
validate,
|
validate,
|
||||||
validateFields,
|
validateFields,
|
||||||
|
|
|
@ -725,3 +725,17 @@ export function chainFunctions(...fns:Array<(...args:Array<any>) => void>):(...a
|
||||||
fns.forEach(fn => fn && fn(...args));
|
fns.forEach(fn => fn && fn(...args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mapObject(value: any, fn: Function): any {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map(item => mapObject(item, fn));
|
||||||
|
}
|
||||||
|
if (isObject(value)) {
|
||||||
|
let tmpValue = {...value};
|
||||||
|
Object.keys(tmpValue).forEach(key => {
|
||||||
|
(tmpValue as PlainObject)[key] = mapObject((tmpValue as PlainObject)[key], fn);
|
||||||
|
});
|
||||||
|
return tmpValue;
|
||||||
|
}
|
||||||
|
return fn(value);
|
||||||
|
}
|
Loading…
Reference in New Issue