Merge remote-tracking branch 'amis/master'

This commit is contained in:
RickCole21 2020-03-09 18:07:29 +08:00
commit ffd9a553cb
80 changed files with 525 additions and 403 deletions

View File

@ -2,74 +2,74 @@
Form 中主要是由各种 FormItem 组成。FormItem 中主要包含这些字段。
- `name` 字段名,表单提交时的 key。
- `value` 值,可以通过它设置默认值。
- `label` 描述标题,当表单为水平布局时,左边即便是不设置 label 为了保持对齐也会留空,如果想要去掉空白,请设置成 `false`
- `description` 描述内容。
- `placeholder` 占位内容。
- `type` 指定表单类型,如: `text`、`textarea`、`date`、`email`等等
- `inline` 是否为 inline 模式。
- `submitOnChange` 是否该表单项值发生变化时就提交当前表单。
- `className` 表单最外层类名。
- `disabled` 当前表单项是否是禁用状态。
- `disabledOn` 通过[表达式](../Types.md#表达式)来配置当前表单项的禁用状态。
- `visible` 是否可见。
- `visibleOn` 通过[表达式](../Types.md#表达式)来配置当前表单项是否显示。
- `hidden` 是否隐藏,不要跟 `visible` `visibleOn` 同时配置
- `hiddenOn` 通过[表达式](../Types.md#表达式)来配置当前表单项是否隐藏。
- `inputClassName` 表单控制器类名。
- `labelClassName` label 的类名。
- `required` 是否为必填。
- `requiredOn` 通过[表达式](../Types.md#表达式)来配置当前表单项是否为必填。
- `validations` 格式验证,支持设置多个,多个规则用英文逗号隔开。
- `name` 字段名,表单提交时的 key。
- `value` 值,可以通过它设置默认值。
- `label` 描述标题,当表单为水平布局时,左边即便是不设置 label 为了保持对齐也会留空,如果想要去掉空白,请设置成 `false`
- `description` 描述内容。
- `placeholder` 占位内容。
- `type` 指定表单类型,如: `text`、`textarea`、`date`、`email`等等
- `inline` 是否为 inline 模式。
- `submitOnChange` 是否该表单项值发生变化时就提交当前表单。
- `className` 表单最外层类名。
- `disabled` 当前表单项是否是禁用状态。
- `disabledOn` 通过[表达式](../Types.md#表达式)来配置当前表单项的禁用状态。
- `visible` 是否可见。
- `visibleOn` 通过[表达式](../Types.md#表达式)来配置当前表单项是否显示。
- `hidden` 是否隐藏,不要跟 `visible` `visibleOn` 同时配置
- `hiddenOn` 通过[表达式](../Types.md#表达式)来配置当前表单项是否隐藏。
- `inputClassName` 表单控制器类名。
- `labelClassName` label 的类名。
- `required` 是否为必填。
- `requiredOn` 通过[表达式](../Types.md#表达式)来配置当前表单项是否为必填。
- `validations` 格式验证,支持设置多个,多个规则用英文逗号隔开。
- `isEmptyString` 必须是空白字符。
- `isEmail` 必须是 Email。
- `isUrl` 必须是 Url。
- `isNumeric` 必须是 数值。
- `isAlpha` 必须是 字母。
- `isAlphanumeric` 必须是 字母或者数字。
- `isInt` 必须是 整形。
- `isFloat` 必须是 浮点形。
- `isLength:length` 是否长度正好等于设定值。
- `minLength:length` 最小长度。
- `maxLength:length` 最大长度。
- `maximum:length` 最大值。
- `minimum:length` 最小值。
- `equals:xxx` 当前值必须完全等于 xxx。
- `equalsField:xxx` 当前值必须与 xxx 变量值一致。
- `isJson` 是否是合法的 Json 字符串。
- `notEmptyString` 要求输入内容不是空白。
- `isUrlPath` 是 url 路径。
- `matchRegexp:/foo/` 必须命中某个正则。
- `matchRegexp1:/foo/` 必须命中某个正则。
- `matchRegexp2:/foo/` 必须命中某个正则。
- `matchRegexp3:/foo/` 必须命中某个正则。
- `matchRegexp4:/foo/` 必须命中某个正则。
如:
```js
{
"validations": "isNumeric,minimum:10",
// 或者对象配置方式, 推荐
"validations": {
"isNumeric": true,
"minimum": 10
}
}
```
- `validationErrors` 自定义错误提示, 配置为对象, key 为规则名, value 为错误提示字符串(提示:其中`$1`表示输入)
- `isEmptyString` 必须是空白字符。
- `isEmail` 必须是 Email。
- `isUrl` 必须是 Url。
- `isNumeric` 必须是 数值。
- `isAlpha` 必须是 字母。
- `isAlphanumeric` 必须是 字母或者数字。
- `isInt` 必须是 整形。
- `isFloat` 必须是 浮点形。
- `isLength:length` 是否长度正好等于设定值。
- `minLength:length` 最小长度。
- `maxLength:length` 最大长度。
- `maximum:number` 最大值。
- `minimum:number` 最小值。
- `equals:xxx` 当前值必须完全等于 xxx。
- `equalsField:xxx` 当前值必须与 xxx 变量值一致。
- `isJson` 是否是合法的 Json 字符串。
- `notEmptyString` 要求输入内容不是空白。
- `isUrlPath` 是 url 路径。
- `matchRegexp:/foo/` 必须命中某个正则。
- `matchRegexp1:/foo/` 必须命中某个正则。
- `matchRegexp2:/foo/` 必须命中某个正则。
- `matchRegexp3:/foo/` 必须命中某个正则。
- `matchRegexp4:/foo/` 必须命中某个正则。
如:
```json
{
"validationErrors": {
"isEmail": "请输入正确的邮箱地址"
}
```js
{
"validations": "isNumeric,minimum:10",
// 或者对象配置方式, 推荐
"validations": {
"isNumeric": true,
"minimum": 10
}
```
- `validateOnChange` 是否修改就验证数值,默认当表单提交过就会每次修改验证,如果要关闭请设置为 `false`,即便是关了,表单提交前还是会验证的。
}
```
- `validationErrors` 自定义错误提示, 配置为对象, key 为规则名, value 为错误提示字符串(提示:其中`$1`表示输入)
如:
```json
{
"validationErrors": {
"isEmail": "请输入正确的邮箱地址"
}
}
```
- `validateOnChange` 是否修改就验证数值,默认当表单提交过就会每次修改验证,如果要关闭请设置为 `false`,即便是关了,表单提交前还是会验证的。
```schema:height="200" scope="form-item"
{

View File

@ -2,27 +2,27 @@
选项表单。
- `type` 请设置成 `select`
- `options` 选项配置,类型为数组,成员格式如下。
- `label` 文字
- `value`
- `value` 设置默认值,如果想要默认选中某个,请设置默认值。
- `source` Api 地址,如果选项不固定,可以通过配置 `source` 动态拉取。另外也可以用 `$xxxx` 来获取当前作用域中的变量。
- `autoComplete` 跟 source 不同的是,每次用户输入都会去接口获取提示。
- `multiple` 默认为 `false`, 设置成 `true` 表示可多选。
- `joinValues` 默认为 `true`
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 `value` 的值封装为数组,作为当前表单项的值。
- `delimiter` 默认为 `,`
- `clearable` 默认为 `false`, 当设置为 `true` 时,已选中的选项右侧会有个小 `X` 用来取消设置。
- `searchable` 默认为 `false`,表示可以通过输入部分内容检索出选项。
- `checkAll` 默认为 `false` 开启后支持全选
- `checkAllLabel` 默认为 `全选`, 全选的文字
- `defaultCheckAll` 是否默认全选,默认为`false`
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- `autoFill`的格式为`{address: "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address` 中
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
- `type` 请设置成 `select`
- `options` 选项配置,类型为数组,成员格式如下。
- `label` 文字
- `value`
- `value` 设置默认值,如果想要默认选中某个,请设置默认值。
- `source` Api 地址,如果选项不固定,可以通过配置 `source` 动态拉取。另外也可以用 `$xxxx` 来获取当前作用域中的变量。
- `autoComplete` 跟 source 不同的是,每次用户输入都会去接口获取提示。
- `multiple` 默认为 `false`, 设置成 `true` 表示可多选。
- `joinValues` 默认为 `true`
- 单选模式:当用户选中某个选项时,选项中的 value 将被作为该表单项的值提交,否则,整个选项对象都会作为该表单项的值提交。
- 多选模式:选中的多个选项的 `value` 会通过 `delimiter` 连接起来,否则直接将以数组的形式提交值。
- `extractValue` 默认为 `false`, `joinValues`设置为`false`时生效, 开启后将选中的选项 `value` 的值封装为数组,作为当前表单项的值。
- `delimiter` 默认为 `,`
- `clearable` 默认为 `false`, 当设置为 `true` 时,已选中的选项右侧会有个小 `X` 用来取消设置。
- `searchable` 默认为 `false`当设置为 `true`表示可以通过输入部分内容检索出选项。
- `checkAll` 默认为 `false` 开启后支持全选
- `checkAllLabel` 默认为 `全选`, 全选的文字
- `defaultCheckAll` 是否默认全选,默认为`false`
- `autoFill` 将当前已选中的选项的某个字段的值自动填充到表单中某个表单项中,只在单选时有效
- `autoFill`的格式为`{address: "${label}"}`,表示将选中项中的`label`的值,自动填充到当前表单项中`name` 为`address` 中
- **还有更多通用配置请参考** [FormItem](./FormItem.md)
单选
@ -91,7 +91,6 @@
]
```
### 接口说明
开始之前请你先阅读[整体要求](../api.md)。
@ -126,4 +125,4 @@
"value": "值" // 默认值,可以获取列表的同时设置默认值。
}
}
```
```

View File

@ -28,24 +28,24 @@ tpl 类型的渲染器支持用 JS 模板引擎来组织输出,采用的 lodas
仔细看示例不难发现,语法跟 ejs 很像,`<% 这里面是 js 语句 %>`,所以只要会写 js做页面渲染没有什么问题。另外以下是一些可用 js 方法。
- `formatDate(value, format='LLL', inputFormat='')` 格式化时间格式,关于 format 请前往 [moment](http://momentjs.com/) 文档页面。
- `formatTimeStamp(value, format='LLL')` 格式化时间戳为字符串。
- `formatNumber(number)` 格式化数字格式,加上千分位。
- `countDown(value)` 倒计时,显示离指定时间还剩下多少天,只支持时间戳。
- 下面 filters 中的方法也可以使用如: `<%= date(data.xxx, 'YYYY-MM-DD')%>`
- 可以联系我们添加更多公用方法。
- `formatDate(value, format='LLL', inputFormat='')` 格式化时间格式,关于 format 请前往 [moment](http://momentjs.com/) 文档页面。
- `formatTimeStamp(value, format='LLL')` 格式化时间戳为字符串。
- `formatNumber(number)` 格式化数字格式,加上千分位。
- `countDown(value)` 倒计时,显示离指定时间还剩下多少天,只支持时间戳。
- 下面 filters 中的方法也可以使用如: `<%= date(data.xxx, 'YYYY-MM-DD')%>`
- 可以联系我们添加更多公用方法。
如:
```json
{
"data": {
"user": "no one"
},
"body": {
"type": "tpl",
"tpl": "User: <%= formatDate(data.time, 'YYYY-MM-DD') %>"
}
"data": {
"user": "no one"
},
"body": {
"type": "tpl",
"tpl": "User: <%= formatDate(data.time, 'YYYY-MM-DD') %>"
}
}
```
@ -65,51 +65,59 @@ tpl 类型的渲染器支持用 JS 模板引擎来组织输出,采用的 lodas
`注意:$xxx 与 <%= data.xxx %> 这两种语法不能同时使用,只有一种有效,所以不要交叉使用。`
通过 `$xxx` 取到的值,默认是会做 html 转义的,也就是说 `$xxx` 完全等价于 `${xxx | html}`, 如果你想什么都不做,那么请这么写 `${xxx | raw}`
通过 `$xxx` 取到的值,默认是会做 html 转义的,也就是说 `$xxx` 完全等价于 `${xxx | html}`, 如果你想什么都不做,那么请这么写 `${xxx | raw}`
从上面的语法可以看出来,取值时是支持指定 filter 的,那么有哪些 filter 呢?
- `html` 转义 html 如:`${xxx|html}`。
- `json` json stringify。
- `raw` 表示不转换, 原样输出。
- `date` 做日期转换如: `${xxx | date:YYYY-MM-DD}`
- `number` 自动给数字加千分位。`${xxx | number}` `9999` => `9,999`
- `trim` 把前后多余的空格去掉。
- `percent` 格式化成百分比。`${xxx | percent}` `0.8232343` => `82.32%`
- `round` 四舍五入取整。
- `truncate` 切除, 当超出 200 个字符时,后面的部分直接显示 ...。 `${desc | truncate:500:...}`
- `url_encode` 做 url encode 转换。
- `url_decode` 做 url decode 转换。
- `default` 当值为空时,显示其他值代替。 `${xxx | default:-}` 当为空时显示 `-`
- `join` 当值是 array 时,可以把内容连起来。\${xxx | join:,}
- `first` 获取数组的第一个成员。
- `last` 获取数组的最后一个成员。
- `pick` 如果是对象则从当前值中再次查找值如: `${xxx|pick:yyy}` 等价于 `${xxx.yyy}`。如果是数组,则做 map 操作,操作完后还是数组,不过成员已经变成了你选择的东西。如: `${xxx|pick:bbb}` 如果 xxx 的值为 `[{aaa: 1, bbb: 2}]` 经过处理后就是 `[2]`。更复杂的用法: `${xxx|pick:a~aaa,b~bbb}` 经过处理就是 `[{a:1, b: 2}]`
- `split` 可以将字符传通过分隔符分离成数组,默认分隔符为 `,` 如: `${ids|split|last}` 即取一段用逗号分割的数值中的最后一个。
- `nth` 取数组中的第 n 个成员。如: `${ids|split|nth:1}`
- `str2date` 请参考 [date](./Date.md) 中日期默认值的设置格式。
- `duration` 格式化成时间端如:`2` -=> `2秒` `67` => `1分7秒` `1111111` => `13天21时39分31秒`
- `asArray` 将数据包成数组如: `a` => `[a]`
- `lowerCase` 转小写
- `upperCase` 转大写
- `base64Encode` base64 转码
- `base64Decode` base64 解码
- `filter` 过滤数组,操作对象为数组,当目标对象不是数组时将无效。使用语法 ${xxx | filter: 参与过滤的字段集合:指令:取值变量名}。比如: `${xxx|filter:readonly:isTrue}` 将xxx 数组中 readonly 为 true 的成员提取出来。再来个栗子:`${xxx|filter:a,b:match:keywords}` 将 xxx 数组中成员变量 a 或者 b 的值与环境中 keywords 的值相匹配的提取出来。如果不需要取变量,也可以写固定值如:`${xxx|filter:a,b:match:'123'}`
- `html` 转义 html 如:`${xxx|html}`。
- `json` json stringify。
- `raw` 表示不转换, 原样输出。
- `date` 做日期转换如: `${xxx | date:YYYY-MM-DD}`
- `number` 自动给数字加千分位。`${xxx | number}` `9999` => `9,999`
- `trim` 把前后多余的空格去掉。
- `percent` 格式化成百分比。`${xxx | percent}` `0.8232343` => `82.32%`
- `round` 四舍五入取整。
- `truncate` 切除, 当超出 200 个字符时,后面的部分直接显示 ...。 `${desc | truncate:500:...}`
- `url_encode` 做 url encode 转换。
- `url_decode` 做 url decode 转换。
- `default` 当值为空时,显示其他值代替。 `${xxx | default:-}` 当为空时显示 `-`
- `join` 当值是 array 时,可以把内容连起来。\${xxx | join:,}
- `first` 获取数组的第一个成员。
- `last` 获取数组的最后一个成员。
- `pick` 如果是对象则从当前值中再次查找值如: `${xxx|pick:yyy}` 等价于 `${xxx.yyy}`。如果是数组,则做 map 操作,操作完后还是数组,不过成员已经变成了你选择的东西。如: `${xxx|pick:bbb}` 如果 xxx 的值为 `[{aaa: 1, bbb: 2}]` 经过处理后就是 `[2]`。更复杂的用法: `${xxx|pick:a~aaa,b~bbb}` 经过处理就是 `[{a:1, b: 2}]`
- `split` 可以将字符传通过分隔符分离成数组,默认分隔符为 `,` 如: `${ids|split|last}` 即取一段用逗号分割的数值中的最后一个。
- `nth` 取数组中的第 n 个成员。如: `${ids|split|nth:1}`
- `str2date` 请参考 [date](./Date.md) 中日期默认值的设置格式。
- `duration` 格式化成时间端如:`2` -=> `2秒` `67` => `1分7秒` `1111111` => `13天21时39分31秒`
- `asArray` 将数据包成数组如: `a` => `[a]`
- `lowerCase` 转小写
- `upperCase` 转大写
- `base64Encode` base64 转码
- `base64Decode` base64 解码
- `filter` 过滤数组,操作对象为数组,当目标对象不是数组时将无效。使用语法 \${xxx | filter: 参与过滤的字段集合:指令:取值变量名}。
比如: `${xxx|filter:readonly:isTrue}` 将 xxx 数组中 readonly 为 true 的成员提取出来。
再来个栗子:`${xxx|filter:a,b:match:keywords}` 将 xxx 数组中成员变量 a 或者 b 的值与环境中 keywords 的值相匹配的提取出来。如果不需要取变量,也可以写固定值如:`${xxx|filter:a,b:match:'123'}`
指令类型:
- `isTrue` 目标值为真通过筛选。
- `isFalse` 目标值为假时通过筛选。
- `match` 模糊匹配后面的参数。`${xxx|filter:a,b:match:keywords}` 表示 xxx 里面的成员,如果字段 a 或者 字段 b 模糊匹配 keywords 变量的值,则通过筛选。
- `equals` 相对于模糊匹配,这个就相对精确匹配了,用法跟 `match` 一样。
组合使用。
- `${&|json|html}` 把当前可用的数据全部打印出来。`$&` 取当前值json 做 json stringify然后 html 转义。
- `${rows|first|pick:id}` 把 rows 中的第一条数据中的 id 取到。
- `${rows|pick:id|join:,}`
- `${&|json|html}` 把当前可用的数据全部打印出来。`$&` 取当前值json 做 json stringify然后 html 转义。
- `${rows|first|pick:id}` 把 rows 中的第一条数据中的 id 取到。
- `${rows|pick:id|join:,}`
没有找到合适的?可以自定义 filter。如果是 AMIS 平台用户,可以将以下代码加入到自定义组件中,如果不是请想办法插入以下代码。
```js
import {registerFilter} from 'amis';
registerFilter('my-filter', (input:string) => `${input}Boom`);
registerFilter('myfilter', (input: string) => `${input}Boom`);
```
加入成功后就可以这样使用了 `${xxx | my-filter}`。 如果 `xxx` 的值是 `abc` 那么输出将会是 `abcBoom`
加入成功后就可以这样使用了 `${xxx | myfilter}`。 如果 `xxx` 的值是 `abc` 那么输出将会是 `abcBoom`

View File

@ -49,11 +49,11 @@ Api 类型可以是字符串或者对象。API 中可以直接设置数据发送
- `<type>` 可以是: `get`、`post`、`put`、`delete`或者`raw`
- `<url>` 即 api 地址,支持通过 `$key` 取变量。
如:
如:
* `get:http://imis.tieba.baidu.com/yule/list?start=$startTime&end=$endTime`
* `get:http://imis.tieba.baidu.com/yule/list?$$` 拿所有可用数据。
* `get:http://imis.tieba.baidu.com/yule/list?data=$$` 拿所有可用数据。
* `get:http://imis.tieba.baidu.com/yule/list?start=$startTime&end=$endTime`
* `get:http://imis.tieba.baidu.com/yule/list?$$` 拿所有可用数据。
* `get:http://imis.tieba.baidu.com/yule/list?data=$$` 拿所有可用数据。
- `Object`
@ -65,6 +65,7 @@ Api 类型可以是字符串或者对象。API 中可以直接设置数据发送
- `headers` 头部,配置方式和 data 配置一样,下面不详讲。如果要使用,请前往群组系统配置中,添加允许。
- `sendOn` 可以配置发送条件比如: `this.id` 表示当存在 id 值时才发送这个请求。
- `cache` 通过配置此属性开启缓存,单位是 ms比如设置 3000 的话,当前接口在 3s 内请求,只要传参一致就会走缓存。
- `replaceData` boolean; 返回的数据是否替换掉当前的数据,默认为 false追加设置成 true 就是完全替换。
- `requestAdaptor` (api) => api; 发送适配器,支持字符串串格式,或者直接就是函数如:
```

View File

@ -1399,13 +1399,12 @@ $Tree-inputHeight: $Form-input-height * 0.85 !default;
// IconPicker
$IconPicker-tabs-bg: #f0f3f4 !default;
$IconPicker-tab-padding: 0 px2rem(5px) !default;
$IconPicker-tab-padding: 0 px2rem(10px) !default;
$IconPicker-tab-height: px2rem(30px) !default;
$IconPicker-tab-lineHeight: px2rem(30px) !default;
$IconPicker-tab-onActive-bg: $white !default;
$IconPicker-content-maxHeight: px2rem(350px) !default;
$IconPicker-singleVendor-padding: px2rem(5px) 0 px2rem(5px) px2rem(13px) !default;
$IconPicker-multiVendor-padding: px2rem(35px) 0 px2rem(5px) px2rem(13px) !default;
$IconPicker-padding: px2rem(5px) !default;
$IconPicker-sugItem-width: px2rem(28px) !default;
$IconPicker-sugItem-height: px2rem(28px) !default;
$IconPicker-sugItem-lineHeight: px2rem(28px) !default;

View File

@ -13,8 +13,6 @@
background: transparent;
border: 0;
float: right;
font-size: 21px;
font-weight: 700;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;

View File

@ -66,6 +66,21 @@
}
}
&-avtarText {
background-color: $primary;
color: $white;
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
vertical-align: middle;
border-radius: 500px;
float: left;
margin-right: $gap-base;
font-size: $fontSizeXl;
text-transform: uppercase();
}
&-meta {
display: block;
height: 100%;

View File

@ -26,6 +26,12 @@
}
.#{$ns}ContextMenu {
z-index: $zindex-contextmenu;
position: fixed;
left: 0;
top: 0;
// pointer-events: none;
&-menu {
position: absolute;
z-index: $zindex-contextmenu;

View File

@ -26,7 +26,7 @@
position: absolute;
left: 50%;
top: 50%;
height: 100%;
max-height: 100%;
width: auto;
transform: translate(-50%, -50%);
}

View File

@ -45,11 +45,6 @@
}
&-tabs {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 11;
background: $IconPicker-tabs-bg;
}
@ -60,24 +55,19 @@
line-height: $IconPicker-tab-lineHeight;
cursor: pointer;
text-align: center;
font-size: $fontSizeSm;
user-select: none;
&.active {
background: $IconPicker-tab-onActive-bg;
}
}
&-singleVendor {
padding: $IconPicker-singleVendor-padding;
}
&-multiVendor {
padding: $IconPicker-multiVendor-padding;
}
&-sugs {
position: relative;
padding: $IconPicker-padding;
max-height: $IconPicker-content-maxHeight;
overflow-y: scroll;
overflow-y: auto;
}
&-sugItem {
@ -87,6 +77,15 @@
text-align: center;
line-height: $IconPicker-sugItem-lineHeight;
cursor: pointer;
&:hover {
background-color: $Form-select-menu-onHover-bg;
}
&.is-active {
color: $white;
background-color: $Form-select-menu-onActive-color;
}
}
&-value {

View File

@ -62,6 +62,7 @@
}
&-itemLabel {
display: flex;
&:hover {
background: $Tree-item-onHover-bg;
}
@ -223,6 +224,7 @@
&-itemText {
cursor: pointer;
flex: 1 auto;
}
&-placeholder {

View File

@ -4,9 +4,9 @@
*/
import React from 'react';
import find = require('lodash/find');
import find from 'lodash/find';
import PropTypes from 'prop-types';
import hoistNonReactStatic = require('hoist-non-react-statics');
import hoistNonReactStatic from 'hoist-non-react-statics';
import qs from 'qs';
import {dataMapping} from './utils/tpl-builtin';
import {RendererEnv, RendererProps} from './factory';

View File

@ -26,6 +26,7 @@ export interface AlertState {
title?: string;
content: string;
confirm: boolean;
confirmText?: string;
}
export class Alert extends React.Component<AlertProps, AlertState> {
@ -115,12 +116,13 @@ export class Alert extends React.Component<AlertProps, AlertState> {
});
}
confirm(content: string, title?: string) {
confirm(content: string, title?: string, confirmText?: string) {
this.setState({
title,
content,
show: true,
confirm: true
confirm: true,
confirmText
});
return new Promise(resolve => {
@ -169,7 +171,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
level={this.state.confirm ? confirmBtnLevel : alertBtnLevel}
onClick={this.handleConfirm}
>
{confirmText}
{this.state.confirmText || confirmText}
</Button>
</div>
</Modal>
@ -181,9 +183,11 @@ export const alert: (content: string, title?: string) => void = (
content,
title
) => Alert.getInstance().alert(content, title);
export const confirm: (content: string, title?: string) => Promise<any> = (
content,
title
) => Alert.getInstance().confirm(content, title);
export const confirm: (
content: string,
title?: string,
confirmText?: string
) => Promise<any> = (content, title, confirmText) =>
Alert.getInstance().confirm(content, title, confirmText);
export const ThemedAlert = themeable(Alert);
export default ThemedAlert;

View File

@ -5,6 +5,7 @@
import React from 'react';
import {ClassNamesFn, themeable} from '../theme';
import {Icon} from './icons';
export interface AlertProps {
level: 'danger' | 'info' | 'success' | 'warning';
@ -70,7 +71,7 @@ export class Alert extends React.Component<AlertProps, AlertState> {
onClick={this.handleClick}
type="button"
>
<span>×</span>
<Icon icon="close" className="icon" />
</button>
) : null}
{children}

View File

@ -5,14 +5,14 @@
*/
import React from "react";
import uncontrollable = require("uncontrollable");
import uncontrollable from "uncontrollable";
import Checkbox from "./Checkbox";
import find = require("lodash/find");
import chunk = require("lodash/chunk");
import find from "lodash/find";
import chunk from "lodash/chunk";
import { flattenTree, isObject } from "../utils/helper";
import { ClassNamesFn, themeable } from "../theme";
import { optionValueCompare } from "./Select";
// import isPlainObject = require('lodash/isPlainObject');
// import isPlainObject from 'lodash/isPlainObject';
export interface Option {
label?: string;

View File

@ -5,7 +5,7 @@
*/
import React from 'react';
import css = require('dom-helpers/style/index');
import css from 'dom-helpers/style/index';
import {ClassNamesFn, themeable} from '../theme';
import Transition, {
EXITED,

View File

@ -10,7 +10,7 @@ import {findDOMNode} from 'react-dom';
import {SketchPicker, GithubPicker, ColorResult} from 'react-color';
import {Icon} from './icons';
import Overlay from './Overlay';
import uncontrollable = require('uncontrollable');
import uncontrollable from 'uncontrollable';
import PopOver from './PopOver';
import {ClassNamesFn, themeable} from '../theme';
import {autobind} from '../utils/helper';
@ -264,9 +264,7 @@ export class ColorControl extends React.PureComponent<
{isOpened ? (
<Overlay
placement={
placement || 'left-bottom-left-top right-bottom-right-top'
}
placement={placement || 'auto'}
target={() => findDOMNode(this)}
onHide={this.close}
container={popOverContainer || (() => findDOMNode(this))}

View File

@ -68,7 +68,9 @@ export class ContextMenu extends React.Component<
};
menuRef: React.RefObject<HTMLDivElement> = React.createRef();
originInstance: this | null;
componentWillMount() {
this.originInstance = ContextMenu.instance;
ContextMenu.instance = this;
}
@ -78,9 +80,10 @@ export class ContextMenu extends React.Component<
}
componentWillUnmount() {
ContextMenu.instance = null;
ContextMenu.instance = this.originInstance;
// document.body.removeEventListener('click', this.handleOutClick, true);
document.removeEventListener('keydown', this.handleKeyDown);
delete this.originInstance;
}
@autobind

View File

@ -4,8 +4,8 @@
* @author fex
*/
import React = require('react');
import moment = require('moment');
import React from 'react';
import moment from 'moment';
import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import {Icon} from './icons';

View File

@ -5,7 +5,7 @@
*/
import React from 'react';
import VisibilitySensor = require('react-visibility-sensor');
import VisibilitySensor from 'react-visibility-sensor';
import Spinner from './Spinner';
export interface LazyComponentProps {

View File

@ -14,10 +14,10 @@
*/
import React from 'react';
import uncontrollable = require('uncontrollable');
import uncontrollable from 'uncontrollable';
import Checkbox from './Checkbox';
import {value2array, OptionProps, Option} from './Checkboxes';
import chunk = require('lodash/chunk');
import chunk from 'lodash/chunk';
import {ClassNamesFn, themeable} from '../theme';
interface RadioProps extends OptionProps {

View File

@ -6,7 +6,7 @@
import React from 'react';
import InputRange from 'react-input-range';
import uncontrollable = require('uncontrollable');
import uncontrollable from 'uncontrollable';
import cx from 'classnames';
import {RendererProps} from '../factory';
import {ClassNamesFn, themeable} from '../theme';

View File

@ -5,7 +5,7 @@
* @date 2017-11-07
*/
import uncontrollable = require('uncontrollable');
import uncontrollable from 'uncontrollable';
import React from 'react';
import 'react-datetime/css/react-datetime.css';
import Overlay from './Overlay';
@ -15,9 +15,9 @@ import {closeIcon, Icon} from './icons';
// @ts-ignore
import matchSorter from 'match-sorter';
import {noop, isObject} from '../utils/helper';
import find = require('lodash/find');
import isPlainObject = require('lodash/isPlainObject');
import union = require('lodash/union');
import find from 'lodash/find';
import isPlainObject from 'lodash/isPlainObject';
import union from 'lodash/union';
import {highlight} from '../renderers/Form/Options';
import {findDOMNode} from 'react-dom';
import {ClassNamesFn, themeable} from '../theme';

View File

@ -4,9 +4,9 @@
* @author fex
*/
import React = require('react');
import React from 'react';
import Html from './Html';
import uncontrollable = require('uncontrollable');
import uncontrollable from 'uncontrollable';
import {findDOMNode} from 'react-dom';
import Tooltip from './Tooltip';
import {ClassNamesFn, themeable} from '../theme';

View File

@ -29,13 +29,13 @@ import {
} from './types';
import {observer} from 'mobx-react';
import getExprProperties from './utils/filter-schema';
import hoistNonReactStatic = require('hoist-non-react-statics');
import omit = require('lodash/omit');
import difference = require('lodash/difference');
import isPlainObject = require('lodash/isPlainObject');
import hoistNonReactStatic from 'hoist-non-react-statics';
import omit from 'lodash/omit';
import difference from 'lodash/difference';
import isPlainObject from 'lodash/isPlainObject';
import Scoped from './Scoped';
import {getTheme, ThemeInstance, ClassNamesFn, ThemeContext} from './theme';
import find = require('lodash/find');
import find from 'lodash/find';
import Alert from './components/Alert2';
import {LazyComponent} from './components';
import ImageGallery from './components/ImageGallery';

View File

@ -2,7 +2,7 @@ import React from 'react';
import {Renderer, RendererProps} from '../factory';
import {filter} from '../utils/tpl';
import Button from '../components/Button';
import pick = require('lodash/pick');
import pick from 'lodash/pick';
const ActionProps = [
'dialog',
'drawer',

View File

@ -1,5 +1,5 @@
import {Renderer, RendererProps} from '../factory';
import React = require('react');
import React from 'react';
import Alert, {AlertProps} from '../components/Alert2';
@Renderer({

View File

@ -1,5 +1,5 @@
import React from 'react';
import upperFirst = require('lodash/upperFirst');
import upperFirst from 'lodash/upperFirst';
import {Renderer, RendererProps} from '../factory';
import {autobind} from '../utils/helper';
import {Icon} from '../components/icons';

View File

@ -14,18 +14,18 @@ import {
qsstringify
} from '../utils/helper';
import {observer} from 'mobx-react';
import partition = require('lodash/partition');
import partition from 'lodash/partition';
import Scoped, {ScopedContext, IScopedContext} from '../Scoped';
import Button from '../components/Button';
import Select from '../components/Select';
import getExprProperties from '../utils/filter-schema';
import pick = require('lodash/pick');
import pick from 'lodash/pick';
import qs from 'qs';
import {findDOMNode} from 'react-dom';
import {evalExpression, filter} from '../utils/tpl';
import {isValidApi, buildApi, isEffectiveApi} from '../utils/api';
import omit = require('lodash/omit');
import find = require('lodash/find');
import omit from 'lodash/omit';
import find from 'lodash/find';
import Html from '../components/Html';
import {Spinner} from '../components';

View File

@ -280,9 +280,12 @@ export class Card extends React.Component<CardProps> {
subTitleClassName,
descClassName,
checkOnItemClick,
avatarClassName,
checkable,
classnames: cx,
classPrefix: ns
classPrefix: ns,
imageClassName,
avatarTextClassName
} = this.props;
let heading = null;
@ -291,8 +294,7 @@ export class Card extends React.Component<CardProps> {
const {
highlight: highlightTpl,
avatar: avatarTpl,
avatarClassName,
imageClassName,
avatarText: avatarTextTpl,
title: titleTpl,
subTitle: subTitleTpl,
subTitlePlaceholder,
@ -302,6 +304,7 @@ export class Card extends React.Component<CardProps> {
const highlight = !!evalExpression(highlightTpl, data as object);
const avatar = filter(avatarTpl, data);
const avatarText = filter(avatarTextTpl, data);
const title = filter(titleTpl, data);
const subTitle = filter(subTitleTpl, data);
const desc = filter(descTpl, data);
@ -323,6 +326,15 @@ export class Card extends React.Component<CardProps> {
src={avatar}
/>
</span>
) : avatarText ? (
<span
className={cx(
'Card-avtarText',
header.avatarTextClassName || avatarTextClassName
)}
>
{avatarText}
</span>
) : null}
<div className={cx('Card-meta')}>
{highlight ? (

View File

@ -13,9 +13,9 @@ import {
ucFirst
} from '../utils/helper';
import {resolveVariable} from '../utils/tpl-builtin';
import Sortable = require('sortablejs');
import Sortable from 'sortablejs';
import {filter} from '../utils/tpl';
import debounce = require('lodash/debounce');
import debounce from 'lodash/debounce';
import {resizeSensor} from '../utils/resize-sensor';
export interface Column {

View File

@ -13,7 +13,8 @@ import {ScopedContext, IScopedContext} from '../Scoped';
export interface ChartProps extends RendererProps {
chartRef?: (echart: any) => void;
onDataFilter?: (config: any) => any;
onDataFilter?: (config: any, echarts: any) => any;
dataFilter?: string;
api?: Api;
source?: string;
config?: object;
@ -193,13 +194,23 @@ export class Chart extends React.Component<ChartProps> {
if (!this.echarts) {
return;
}
const onDataFilter = this.props.onDataFilter;
let onDataFilter = this.props.onDataFilter;
const dataFilter = this.props.dataFilter;
if (!onDataFilter && typeof dataFilter === 'string') {
onDataFilter = new Function('config', 'echarts', dataFilter) as any;
}
config = config || this.pending;
if (typeof config === 'string') {
config = new Function('return ' + config)();
}
onDataFilter && (config = onDataFilter(config) || config);
try {
onDataFilter &&
(config = onDataFilter(config, (window as any).echarts) || config);
} catch (e) {
console.warn(e);
}
if (config) {
try {

View File

@ -6,7 +6,7 @@
import React from 'react';
import {RendererProps} from '../factory';
import cx from 'classnames';
import hoistNonReactStatic = require('hoist-non-react-statics');
import hoistNonReactStatic from 'hoist-non-react-statics';
import Button from '../components/Button';
import {filter} from '../utils/tpl';

View File

@ -4,7 +4,7 @@ import {Renderer, RendererProps} from '../factory';
import {SchemaNode, Schema, Action} from '../types';
import {filter} from '../utils/tpl';
import Modal from '../components/Modal';
import findLast = require('lodash/findLast');
import findLast from 'lodash/findLast';
import {guid, isVisible} from '../utils/helper';
import {reaction} from 'mobx';
import {Icon} from '../components/icons';
@ -300,7 +300,8 @@ export default class Dialog extends React.Component<DialogProps, DialogState> {
key,
disabled: (body && (body as any).disabled) || store.loading,
onAction: this.handleAction,
onFinished: this.handleChildFinished
onFinished: this.handleChildFinished,
affixOffsetTop: 0
};
if (!(body as Schema).type) {

View File

@ -3,7 +3,7 @@ import {ScopedContext, IScopedContext} from '../Scoped';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode, Schema, Action} from '../types';
import {default as DrawerContainer} from '../components/Drawer';
import findLast = require('lodash/findLast');
import findLast from 'lodash/findLast';
import {guid, isVisible} from '../utils/helper';
import {reaction} from 'mobx';
import {findDOMNode} from 'react-dom';

View File

@ -2,7 +2,7 @@ import React from 'react';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import cx from 'classnames';
import Checkbox from '../../components/Checkbox';
import chunk = require('lodash/chunk');
import chunk from 'lodash/chunk';
export interface CheckboxesProps extends OptionsControlProps {
placeholder?: any;

View File

@ -14,9 +14,9 @@ import {
autobind,
isObjectShallowModified
} from '../../utils/helper';
import Sortable = require('sortablejs');
import Sortable from 'sortablejs';
import {evalExpression, filter} from '../../utils/tpl';
import find = require('lodash/find');
import find from 'lodash/find';
import Select from '../../components/Select';
import {dataMapping, resolveVariable} from '../../utils/tpl-builtin';
import {isEffectiveApi} from '../../utils/api';

View File

@ -1,6 +1,6 @@
import React from 'react';
import {IFormStore, IFormItemStore} from '../../store/form';
import debouce = require('lodash/debounce');
import debouce from 'lodash/debounce';
import {RendererProps, Renderer} from '../../factory';
import {ComboStore, IComboStore, IUniqueGroup} from '../../store/combo';

View File

@ -4,7 +4,7 @@ import {FormItem, FormControlProps} from './Item';
import {filter} from '../../utils/tpl';
import cx from 'classnames';
import LazyComponent from '../../components/LazyComponent';
import debouce = require('lodash/debounce');
import debouce from 'lodash/debounce';
import {isPureVariable} from '../../utils/tpl-builtin';
function loadComponent(): Promise<React.ReactType> {

View File

@ -1,7 +1,7 @@
import React from 'react';
import {FormItem, FormControlProps} from './Item';
import LazyComponent from '../../components/LazyComponent';
import debouce = require('lodash/debounce');
import debouce from 'lodash/debounce';
import Editor from '../../components/Editor';
export interface EditorProps extends FormControlProps {

View File

@ -2,8 +2,8 @@ import React from 'react';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import qs from 'qs';
import find = require('lodash/find');
import isPlainObject = require('lodash/isPlainObject');
import find from 'lodash/find';
import isPlainObject from 'lodash/isPlainObject';
import {mapLimit} from 'async';
import ImageControl from './Image';
import {Payload, ApiObject, ApiString} from '../../types';
@ -424,6 +424,7 @@ export default class FileControl extends React.Component<FileProps, FileState> {
newFile.error = error;
} else {
newFile = obj as FileValue;
newFile.name = newFile.name || file!.name;
}
files.splice(idx, 1, newFile);
this.current = null;

View File

@ -3,7 +3,7 @@ import Grid, {ColumnNode, Column, ColProps, ColumnArray} from '../Grid';
import {Schema} from '../../types';
import {FormItem, FormControlProps} from './Item';
import pick = require('lodash/pick');
import pick from 'lodash/pick';
import React from 'react';
import cx from 'classnames';

View File

@ -8,6 +8,7 @@ import {
} from '../../utils/helper';
import cx from 'classnames';
import {FormItemWrap} from './Item';
import getExprProperties from '../../utils/filter-schema';
export interface InputGroupProps extends RendererProps {
formMode?: string;
@ -27,7 +28,7 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
}
renderControl(control: any, index: any, otherProps?: any) {
const {render, disabled} = this.props;
const {render, disabled, data} = this.props;
if (!control) {
return null;
@ -43,6 +44,12 @@ export class ControlGroupRenderer extends React.Component<InputGroupProps> {
if (subSchema.control) {
let control = subSchema.control as Schema;
control = subSchema.control = {
...control,
...getExprProperties(control, data)
};
control.hiddenOn && (subSchema.hiddenOn = control.hiddenOn);
control.visibleOn && (subSchema.visibleOn = control.visibleOn);
}

View File

@ -204,17 +204,10 @@ export default class IconPickerControl extends React.PureComponent<
classnames: cx,
name,
value,
noDataTip,
formItem
noDataTip
} = this.props;
const options = this.formatOptions();
const vendors = this.getVendors();
const selectedOptions =
formItem && formItem.selectedOptions.length
? formItem.selectedOptions
: value
? [{label: value, value: value}]
: [];
return (
<Downshift
@ -223,16 +216,13 @@ export default class IconPickerControl extends React.PureComponent<
onChange={this.handleChange}
onOuterClick={this.handleBlur}
onStateChange={this.handleStateChange}
selectedItem={selectedOptions.map((option: Option) => option['value'])}
selectedItem={[value]}
>
{({getInputProps, getItemProps, isOpen, inputValue, selectedItem}) => {
{({getInputProps, getItemProps, isOpen, inputValue}) => {
let filteredOptions =
inputValue && isOpen
? matchSorter(options, inputValue, {keys: ['label', 'value']})
: options;
filteredOptions = filteredOptions.filter(
(option: any) => !~selectedItem.indexOf(option.value)
);
return (
<div
@ -246,21 +236,17 @@ export default class IconPickerControl extends React.PureComponent<
onClick={this.handleClick}
>
<div className={cx('IconPickerControl-valueWrap')}>
{placeholder &&
!selectedOptions.length &&
!this.state.inputValue ? (
{placeholder && !value && !this.state.inputValue ? (
<div className={cx('IconPickerControl-placeholder')}>
{placeholder}
</div>
) : null}
{selectedOptions.map((option: Option, index: number) =>
inputValue && isOpen ? null : (
<div className={cx('IconPickerControl-value')} key={index}>
<i className={cx(`${option.value}`)} />
{option.label}
</div>
)
{!value || (inputValue && isOpen) ? null : (
<div className={cx('IconPickerControl-value')}>
<i className={cx(value)} />
{value}
</div>
)}
<input
@ -269,7 +255,8 @@ export default class IconPickerControl extends React.PureComponent<
ref: this.inputRef,
onFocus: this.handleFocus,
onChange: this.handleInputChange,
onKeyDown: this.handleKeyDown
onKeyDown: this.handleKeyDown,
value: this.state.inputValue
})}
autoComplete="off"
/>
@ -305,7 +292,9 @@ export default class IconPickerControl extends React.PureComponent<
<div
{...getItemProps({
item: option.value,
className: cx(`IconPickerControl-sugItem`)
className: cx(`IconPickerControl-sugItem`, {
'is-active': value === option.value
})
})}
key={index}
>

View File

@ -4,7 +4,7 @@ import {FormItem, FormControlProps} from './Item';
import Cropper from 'react-cropper';
import DropZone from 'react-dropzone';
import 'blueimp-canvastoblob';
import find = require('lodash/find');
import find from 'lodash/find';
import qs from 'qs';
import {Payload} from '../../types';
import {buildApi} from '../../utils/api';
@ -31,6 +31,7 @@ export interface ImageProps extends FormControlProps {
aspectRatio?: number;
aspectRatioLabel?: string;
};
reCropable: boolean;
crop?:
| boolean
| {
@ -487,8 +488,6 @@ export default class ImageControl extends React.Component<
}
editImage(index: number) {
const {multiple} = this.props;
const files = this.files;
this.setState({
@ -569,8 +568,6 @@ export default class ImageControl extends React.Component<
}
return this.setState({
locked: true,
lockedReason: '请选择放弃或者应用',
cropFile: file
});
}
@ -853,6 +850,11 @@ export default class ImageControl extends React.Component<
validate(): any {
if (this.state.locked && this.state.lockedReason) {
return this.state.lockedReason;
} else if (this.state.cropFile) {
return new Promise(resolve => {
this.resolve = resolve;
this.handleCrop();
});
} else if (
this.state.uploading ||
this.files.some(item => item.state === 'pending')
@ -878,7 +880,8 @@ export default class ImageControl extends React.Component<
autoUpload,
hideUploadButton,
thumbMode,
thumbRatio
thumbRatio,
reCropable
} = this.props;
const {files, error, crop, uploading, cropFile} = this.state;
@ -1071,7 +1074,9 @@ export default class ImageControl extends React.Component<
<Icon icon="view" className="icon" />
</a>
{!!crop && !disabled ? (
{!!crop &&
reCropable !== false &&
!disabled ? (
<a
data-tooltip="裁剪图片"
data-position="bottom"

View File

@ -1,5 +1,5 @@
import React from 'react';
import hoistNonReactStatic = require('hoist-non-react-statics');
import hoistNonReactStatic from 'hoist-non-react-statics';
import {IFormItemStore, IFormStore} from '../../store/form';
import {reaction} from 'mobx';

View File

@ -9,6 +9,7 @@ import {FormControlProps, FormItem} from './Item';
import {buildApi, isValidApi, isEffectiveApi} from '../../utils/api';
import {Checkbox, Spinner} from '../../components';
import {autobind, setVariable} from '../../utils/helper';
import {ApiObject} from '../../types';
export interface Column {
label: string;
@ -170,13 +171,12 @@ export default class MatrixCheckbox extends React.Component<
columns: (ret.data as any).columns || []
},
() => {
let replace = source && (source as ApiObject).replaceData;
let value = (ret.data as any).value;
if (value) {
value = mergeValue(
value,
this.state.columns,
this.state.rows
);
value = (source as ApiObject).replaceData
? value
: mergeValue(value, this.state.columns, this.state.rows);
onChange(value);
}
resolve();

View File

@ -1,6 +1,6 @@
import React from 'react';
import xorBy = require('lodash/xorBy');
import unionBy = require('lodash/unionBy');
import xorBy from 'lodash/xorBy';
import unionBy from 'lodash/unionBy';
import Overlay from '../../components/Overlay';
import Checkbox from '../../components/Checkbox';
import PopOver from '../../components/PopOver';

View File

@ -3,7 +3,7 @@ import {OptionsControl, OptionsControlProps, Option} from './Options';
import cx from 'classnames';
import Button from '../../components/Button';
import {SchemaNode, Schema, Action} from '../../types';
import find = require('lodash/find');
import find from 'lodash/find';
import {
anyChanged,
autobind,
@ -11,7 +11,7 @@ import {
noop,
createObject
} from '../../utils/helper';
import findIndex = require('lodash/findIndex');
import findIndex from 'lodash/findIndex';
import Html from '../../components/Html';
import {filter} from '../../utils/tpl';
import {Icon} from '../../components/icons';
@ -92,13 +92,15 @@ export default class PickerControl extends React.PureComponent<
fetchOptions() {
const {value, formItem, valueField, labelField, source, data} = this.props;
let selectedOptions: any;
if (
!source ||
!formItem ||
!formItem.selectedOptions.length ||
formItem.selectedOptions[0][valueField || 'value'] !==
formItem.selectedOptions[0][labelField || 'label']
((selectedOptions = formItem.getSelectedOptions(value)) &&
(!selectedOptions.length ||
selectedOptions[0][valueField || 'value'] !==
selectedOptions[0][labelField || 'label']))
) {
return;
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import isNumber = require('lodash/isNumber');
import isObject = require('lodash/isObject');
import isEqual = require('lodash/isEqual');
import isNumber from 'lodash/isNumber';
import isObject from 'lodash/isObject';
import isEqual from 'lodash/isEqual';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import InputRange from '../../components/Range';

View File

@ -2,8 +2,8 @@ import React from 'react';
import cx from 'classnames';
import {OptionsControl, OptionsControlProps, Option} from './Options';
import Select from '../../components/Select';
import find = require('lodash/find');
import debouce = require('lodash/debounce');
import find from 'lodash/find';
import debouce from 'lodash/debounce';
import {Api} from '../../types';
import {isEffectiveApi} from '../../utils/api';
import {isEmpty, createObject} from '../../utils/helper';

View File

@ -1,8 +1,8 @@
import React from 'react';
import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import omit = require('lodash/omit');
import pick = require('lodash/pick');
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import {createObject} from '../../utils/helper';
export interface SubFormProps extends FormControlProps {

View File

@ -3,13 +3,13 @@ import {FormItem, FormControlProps} from './Item';
import cx from 'classnames';
import Button from '../../components/Button';
import {createObject, isObjectShallowModified} from '../../utils/helper';
import {RendererData, Action, Api, Payload} from '../../types';
import {RendererData, Action, Api, Payload, ApiObject} from '../../types';
import {isEffectiveApi} from '../../utils/api';
import {filter} from '../../utils/tpl';
import omit = require('lodash/omit');
import omit from 'lodash/omit';
import {dataMapping} from '../../utils/tpl-builtin';
import findIndex = require('lodash/findIndex');
import memoize = require('lodash/memoize');
import findIndex from 'lodash/findIndex';
import memoize from 'lodash/memoize';
export interface TableProps extends FormControlProps {
placeholder?: string;
@ -255,7 +255,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
return;
} else if (remote && remote.ok) {
item = {
...item,
...((isNew ? addApi : updateApi) as ApiObject).replaceData ? {} : item,
...remote.data
};
}

View File

@ -4,8 +4,8 @@ import cx from 'classnames';
import {Action} from '../../types';
import Downshift from 'downshift';
import matchSorter from 'match-sorter';
import debouce = require('lodash/debounce');
import find = require('lodash/find');
import debouce from 'lodash/debounce';
import find from 'lodash/find';
import {Icon} from '../../components/icons';
import {Portal} from 'react-overlays';
import {findDOMNode} from 'react-dom';

View File

@ -5,9 +5,9 @@ import {Action} from '../../types';
import Downshift, {StateChangeOptions} from 'downshift';
// @ts-ignore
import matchSorter from 'match-sorter';
import debouce = require('lodash/debounce');
import debouce from 'lodash/debounce';
import {filter} from '../../utils/tpl';
import find = require('lodash/find');
import find from 'lodash/find';
import {Icon} from '../../components/icons';
import Input from '../../components/Input';
import {autobind, createObject, setVariable} from '../../utils/helper';

View File

@ -8,8 +8,8 @@ import {Icon} from '../../components/icons';
import TreeSelector from '../../components/Tree';
// @ts-ignore
import matchSorter from 'match-sorter';
import debouce = require('lodash/debounce');
import find = require('lodash/find');
import debouce from 'lodash/debounce';
import find from 'lodash/find';
import {Api} from '../../types';
import {isEffectiveApi} from '../../utils/api';

View File

@ -16,16 +16,16 @@ import {
isVisible,
cloneObject
} from '../../utils/helper';
import debouce = require('lodash/debounce');
import flatten = require('lodash/flatten');
import find = require('lodash/find');
import debouce from 'lodash/debounce';
import flatten from 'lodash/flatten';
import find from 'lodash/find';
import Scoped, {
ScopedContext,
IScopedContext,
ScopedComponentType
} from '../../Scoped';
import {IComboStore} from '../../store/combo';
import qs = require('qs');
import qs from 'qs';
import {dataMapping} from '../../utils/tpl-builtin';
import {isApiOutdated, isEffectiveApi} from '../../utils/api';
import Spinner from '../../components/Spinner';
@ -328,6 +328,9 @@ export default class Form extends React.Component<FormProps, object> {
async onInit() {
const {onInit, store, submitOnInit} = this.props;
if (!isAlive(store)) {
return;
}
// 先拿出来数据,主要担心 form 被什么东西篡改了,然后又应用出去了
// 之前遇到过问题,所以拿出来了。但是 options loadOptions 默认值失效了。
@ -339,7 +342,11 @@ export default class Form extends React.Component<FormProps, object> {
const hooks: Array<(data: any) => Promise<any>> = this.hooks['init'] || [];
await Promise.all(hooks.map(hook => hook(data)));
if (isAlive(store) && store.initedAt !== initedAt) {
if (!isAlive(store)) {
return;
}
if (store.initedAt !== initedAt) {
// 说明,之前的数据已经失效了。
// 比如 combo 一开始设置了初始值,然后 form 的 initApi 又返回了新的值。
// 这个时候 store 的数据应该已经 init 了新的值。但是 data 还是老的,这个时候

View File

@ -2,7 +2,7 @@ import React from 'react';
import {Renderer, RendererProps} from '../factory';
import {Schema} from '../types';
import cx from 'classnames';
import pick = require('lodash/pick');
import pick from 'lodash/pick';
export const ColProps = ['lg', 'md', 'sm', 'xs'];

View File

@ -19,7 +19,7 @@ import {
import {resolveVariable} from '../utils/tpl-builtin';
import QuickEdit from './QuickEdit';
import PopOver from './PopOver';
import Sortable = require('sortablejs');
import Sortable from 'sortablejs';
import {TableCell} from './Table';
import Copyable from './Copyable';

View File

@ -7,7 +7,7 @@ import React from 'react';
import {findDOMNode} from 'react-dom';
import {RendererProps} from '../factory';
import cx from 'classnames';
import hoistNonReactStatic = require('hoist-non-react-statics');
import hoistNonReactStatic from 'hoist-non-react-statics';
import {RootCloseWrapper} from 'react-overlays';
import PopOver, {Offset} from '../components/PopOver';
import Overlay from '../components/Overlay';

View File

@ -3,7 +3,7 @@ import cx from 'classnames';
import {Renderer, RendererProps} from '../factory';
import {FormItem, FormControlProps} from './Form/Item';
import {filter} from '../utils/tpl';
import QrCode = require('qrcode.react');
import QrCode from 'qrcode.react';
export interface QRCodeProps extends FormControlProps {
codeSize?: number;

View File

@ -5,16 +5,16 @@
import React from 'react';
import {findDOMNode} from 'react-dom';
import find = require('lodash/find');
import find from 'lodash/find';
import PropTypes from 'prop-types';
import isPlainObject = require('lodash/isPlainObject');
import isPlainObject from 'lodash/isPlainObject';
import {RendererProps} from '../factory';
import cx from 'classnames';
import hoistNonReactStatic = require('hoist-non-react-statics');
import hoistNonReactStatic from 'hoist-non-react-statics';
import onClickOutside from 'react-onclickoutside';
import {Action} from '../types';
import keycode from 'keycode';
import matches = require('dom-helpers/query/matches');
import matches from 'dom-helpers/query/matches';
import Overlay from '../components/Overlay';
import PopOver from '../components/PopOver';

View File

@ -2,7 +2,7 @@ import React from 'react';
import {findDOMNode} from 'react-dom';
import {Renderer, RendererProps} from '../factory';
import {SchemaNode, Action, Schema, Api, ApiObject} from '../types';
import forEach = require('lodash/forEach');
import forEach from 'lodash/forEach';
import {filter} from '../utils/tpl';
import cx from 'classnames';
import DropDownButton from './DropDownButton';
@ -25,15 +25,15 @@ import {
buildApi,
normalizeApi
} from '../utils/api';
import debounce = require('lodash/debounce');
import xor = require('lodash/xor');
import debounce from 'lodash/debounce';
import xor from 'lodash/xor';
import QuickEdit from './QuickEdit';
import PopOver from '../components/PopOver';
import Copyable from './Copyable';
import Sortable = require('sortablejs');
import flatMap = require('lodash/flatMap');
import Sortable from 'sortablejs';
import flatMap from 'lodash/flatMap';
import {resizeSensor} from '../utils/resize-sensor';
import find = require('lodash/find');
import find from 'lodash/find';
import Overlay from '../components/Overlay';
import PopOverable from './PopOver';
@ -524,7 +524,8 @@ export default class Table extends React.Component<TableProps, object> {
const ns = this.props.classPrefix;
const dom = findDOMNode(this) as HTMLElement;
const clip = (this.table as HTMLElement).getBoundingClientRect();
const offsetY = this.props.env.affixOffsetTop || 0;
const offsetY =
this.props.affixOffsetTop ?? this.props.env.affixOffsetTop ?? 0;
const affixed = clip.top < offsetY && clip.top + clip.height - 40 > offsetY;
const affixedDom = dom.querySelector(`.${ns}Table-fixedTop`) as HTMLElement;

View File

@ -1,9 +1,9 @@
import React from 'react';
import {Renderer, RendererProps} from '../factory';
import {Action, Schema, SchemaNode} from '../types';
import find = require('lodash/find');
import find from 'lodash/find';
import {isVisible, autobind, isDisabled} from '../utils/helper';
import findIndex = require('lodash/findIndex');
import findIndex from 'lodash/findIndex';
import {Tabs as CTabs, Tab} from '../components/Tabs';
import {ClassNamesFn} from '../theme';

View File

@ -3,8 +3,8 @@ import {Renderer, RendererProps} from '../factory';
import {ServiceStore, IServiceStore} from '../store/service';
import cx from 'classnames';
import getExprProperties from '../utils/filter-schema';
import {Api, Payload} from '../types';
import update = require('react-addons-update');
import {Api, ApiObject, Payload} from '../types';
import update from 'react-addons-update';
import {isEffectiveApi, isApiOutdated} from '../utils/api';
import {ScopedContext, IScopedContext} from '../Scoped';
@ -210,10 +210,11 @@ export default class Task extends React.Component<TaskProps, TaskState> {
if (Array.isArray(ret.data)) {
this.handleLoaded(ret);
} else {
let replace = api && (api as ApiObject).replaceData;
const items = this.state.items.map(item =>
item.key === ret.data.key
? {
...item,
...((api as ApiObject).replaceData ? {} : item),
...ret.data
}
: item

View File

@ -67,23 +67,13 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
unSensor: Function;
affixDom: React.RefObject<HTMLDivElement> = React.createRef();
footerDom: React.RefObject<HTMLDivElement> = React.createRef();
initalValues: {
[propName: string]: any;
} = {};
constructor(props: WizardProps) {
super(props);
this.state = {
currentStep: -1 // init 完后会设置成 1
};
this.handleAction = this.handleAction.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleDialogConfirm = this.handleDialogConfirm.bind(this);
this.handleDialogClose = this.handleDialogClose.bind(this);
this.formRef = this.formRef.bind(this);
this.domRef = this.domRef.bind(this);
this.getPopOverContainer = this.getPopOverContainer.bind(this);
}
state = {
currentStep: -1 // init 完后会设置成 1
};
componentDidMount() {
const {
@ -227,6 +217,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
});
}
@autobind
formRef(ref: any) {
if (ref) {
while (ref && ref.getWrappedInstance) {
@ -316,10 +307,12 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
this.reload();
}
@autobind
domRef(ref: any) {
this.dom = ref;
}
@autobind
getPopOverContainer() {
return this.dom;
}
@ -357,6 +350,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
});
}
@autobind
handleAction(
e: React.UIEvent<any> | void,
action: Action,
@ -425,13 +419,35 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
});
}
@autobind
handleChange(values: object) {
const {store} = this.props;
store.updateData(values);
}
@autobind
handleInit(values: any) {
const step = this.state.currentStep;
this.initalValues[step] = this.initalValues[step] || values;
}
@autobind
handleReset(values: any) {
const {store} = this.props;
const initalValue = this.initalValues[this.state.currentStep];
const reseted: any = {};
Object.keys(values).forEach(key => {
reseted[key] = initalValue.hasOwnProperty(key)
? initalValue[key]
: undefined;
});
store.updateData(reseted);
}
// 接管里面 form 的提交,不能直接让 form 提交,因为 wizard 自己需要知道进度。
@autobind
handleSubmit(values: object, action: Action) {
const {
store,
@ -556,6 +572,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
return false;
}
@autobind
handleDialogConfirm(values: object[], action: Action, targets: Array<any>) {
const {store} = this.props;
@ -571,6 +588,7 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
store.closeDialog();
}
@autobind
handleDialogClose() {
const {store} = this.props;
store.closeDialog();
@ -768,6 +786,8 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
{
key: this.state.currentStep,
ref: this.formRef,
onInit: this.handleInit,
onReset: this.handleReset,
onSubmit: this.handleSubmit,
onAction: this.handleAction,
disabled: store.loading,

View File

@ -16,9 +16,9 @@ import {
isEmpty,
qsstringify
} from '../utils/helper';
import {Api, Payload, fetchOptions, Action} from '../types';
import {Api, Payload, fetchOptions, Action, ApiObject} from '../types';
import qs from 'qs';
import pick = require('lodash/pick');
import pick from 'lodash/pick';
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
class ServerError extends Error {
@ -123,7 +123,7 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
syncResponse2Query?: boolean;
}
) => Promise<any> = flow(function* getInitData(
api: string,
api: Api,
data: object,
options: fetchOptions & {
forceReload?: boolean;
@ -265,7 +265,7 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
}
const data = {
...self.pristine,
...((api as ApiObject).replaceData ? {} : self.pristine),
items: rowsData,
count: count,
total: total,
@ -288,7 +288,7 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
}
self.items.replace(rowsData);
self.reInitData(data);
self.reInitData(data, !!(api as ApiObject).replaceData);
options.syncResponse2Query !== false &&
updateQuery(
pick(rest, Object.keys(self.query)),
@ -350,7 +350,7 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* saveRemote(
api: string,
api: Api,
data: object,
options: fetchOptions = {}
) {
@ -369,9 +369,13 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
self.markSaving(false);
if (!isEmpty(json.data) || json.ok) {
self.updateData(json.data, {
__saved: Date.now()
});
self.updateData(
json.data,
{
__saved: Date.now()
},
!!api && (api as ApiObject).replaceData
);
self.updatedAt = Date.now();
}

View File

@ -1,8 +1,8 @@
import {types, getEnv, flow, getRoot, detach} from 'mobx-state-tree';
import debounce = require('lodash/debounce');
import debounce from 'lodash/debounce';
import {ServiceStore} from './service';
import {FormItemStore, IFormItemStore, SFormItemStore} from './formItem';
import {Api, fetchOptions, Payload} from '../types';
import {Api, ApiObject, fetchOptions, Payload} from '../types';
import {ServerError} from '../utils/errors';
import {
getVariable,
@ -12,12 +12,11 @@ import {
createObject,
difference,
guid,
isObject,
isEmpty,
mapObject
} from '../utils/helper';
import {IComboStore} from './combo';
import isEqual = require('lodash/isEqual');
import isEqual from 'lodash/isEqual';
import {IRendererStore} from '.';
export const FormStore = ServiceStore.named('FormStore')
@ -81,8 +80,8 @@ export const FormStore = ServiceStore.named('FormStore')
}
}))
.actions(self => {
function setValues(values: object, tag?: object) {
self.updateData(values, tag);
function setValues(values: object, tag?: object, replace?: boolean) {
self.updateData(values, tag, replace);
// 同步 options
syncOptions();
@ -198,7 +197,7 @@ export const FormStore = ServiceStore.named('FormStore')
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* saveRemote(
api: string,
api: Api,
data: object,
options: fetchOptions = {}
) {
@ -227,11 +226,15 @@ export const FormStore = ServiceStore.named('FormStore')
options
);
// 失败也同样 merge,如果有数据的话。
// 失败也同样修改数据,如果有数据的话。
if (!isEmpty(json.data) || json.ok) {
setValues(json.data, {
__saved: Date.now()
});
setValues(
json.data,
{
__saved: Date.now()
},
!!(api as ApiObject).replaceData
);
self.updatedAt = Date.now();
}

View File

@ -11,7 +11,7 @@ import {str2rules, validate as doValidate} from '../utils/validations';
import {Api, Payload, fetchOptions} from '../types';
import {ComboStore, IComboStore, IUniqueGroup} from './combo';
import {evalExpression} from '../utils/tpl';
import findIndex = require('lodash/findIndex');
import findIndex from 'lodash/findIndex';
import {
isArrayChildrenModified,
isObject,
@ -22,9 +22,9 @@ import {
import {flattenTree} from '../utils/helper';
import {IRendererStore} from '.';
import {normalizeOptions, optionValueCompare} from '../components/Select';
import find = require('lodash/find');
import find from 'lodash/find';
import {SimpleMap} from '../utils/SimpleMap';
import memoize = require('lodash/memoize');
import memoize from 'lodash/memoize';
interface IOption {
value?: string | number | null;

View File

@ -62,17 +62,17 @@ export const iRendererStore = types
self.data = self.pristine;
},
updateData(data: object = {}, tag?: object) {
updateData(data: object = {}, tag?: object, replace?:boolean) {
const prev = self.data;
let newData;
if (tag) {
let proto = createObject((self.data as any).__super || null, tag);
newData = createObject(proto, {
...self.data,
...(replace ? {} : self.data),
...data
});
} else {
newData = extendObject(self.data, data);
newData = extendObject(self.data, data, !replace);
}
Object.defineProperty(newData, '__prev', {

View File

@ -7,8 +7,8 @@ import {
getRoot
} from 'mobx-state-tree';
import {iRendererStore} from './iRenderer';
import isEqual = require('lodash/isEqual');
import find = require('lodash/find');
import isEqual from 'lodash/isEqual';
import find from 'lodash/find';
import {createObject, isObject, guid} from '../utils/helper';
import {evalExpression} from '../utils/tpl';

View File

@ -46,8 +46,8 @@ export const ServiceStore = iRendererStore
self.busying = busying;
}
function reInitData(data: object | undefined) {
const newData = extendObject(self.pristine, data);
function reInitData(data: object | undefined, replace: boolean = false) {
const newData = extendObject(self.pristine, data, !replace);
self.data = self.pristine = newData;
}
@ -65,7 +65,7 @@ export const ServiceStore = iRendererStore
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* getInitData(
api: string,
api: Api,
data: object,
options?: fetchOptions
) {
@ -104,10 +104,12 @@ export const ServiceStore = iRendererStore
: undefined
);
} else {
reInitData({
...self.data,
let replace = !!(api as ApiObject).replaceData;
let data = {
...(replace ? {} : self.data),
...json.data
});
};
reInitData(data, replace);
self.updatedAt = Date.now();
self.hasRemoteData = true;
if (options && options.onSuccess) {
@ -150,7 +152,7 @@ export const ServiceStore = iRendererStore
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* getInitData(
api: string,
api: Api,
data: object,
options?: fetchOptions
) {
@ -175,7 +177,12 @@ export const ServiceStore = iRendererStore
fetchCancel = null;
if (!isEmpty(json.data) || json.ok) {
json.data && self.updateData(json.data);
json.data &&
self.updateData(
json.data,
undefined,
!!(api as ApiObject).replaceData
);
self.updatedAt = Date.now();
self.hasRemoteData = true;
}
@ -233,7 +240,7 @@ export const ServiceStore = iRendererStore
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* saveRemote(
api: string,
api: Api,
data: object,
options: fetchOptions = {}
) {
@ -255,7 +262,12 @@ export const ServiceStore = iRendererStore
);
if (!isEmpty(json.data) || json.ok) {
json.data && self.updateData(json.data);
json.data &&
self.updateData(
json.data,
undefined,
!!(api as ApiObject).replaceData
);
self.updatedAt = Date.now();
}
@ -309,7 +321,7 @@ export const ServiceStore = iRendererStore
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* fetchSchema(
api: string,
api: Api,
data: object,
options: fetchOptions = {}
) {
@ -370,7 +382,12 @@ export const ServiceStore = iRendererStore
if (json.data) {
self.schema = json.data;
self.schemaKey = '' + Date.now();
isObject(json.data.data) && self.updateData(json.data.data);
isObject(json.data.data) &&
self.updateData(
json.data.data,
undefined,
!!(api as ApiObject).replaceData
);
}
updateMessage(json.msg || (options && options.successMessage));
@ -404,7 +421,7 @@ export const ServiceStore = iRendererStore
data?: object,
options?: fetchOptions
) => Promise<any> = flow(function* checkRemote(
api: string,
api: Api,
data: object,
options?: fetchOptions
) {
@ -419,7 +436,12 @@ export const ServiceStore = iRendererStore
data,
options
);
json.ok && self.updateData(json.data);
json.ok &&
self.updateData(
json.data,
undefined,
!!(api as ApiObject).replaceData
);
if (!json.ok) {
throw new Error(json.msg);

View File

@ -10,8 +10,8 @@ import {
} from 'mobx-state-tree';
import {iRendererStore} from './iRenderer';
import {resolveVariable} from '../utils/tpl-builtin';
import isEqual = require('lodash/isEqual');
import find = require('lodash/find');
import isEqual from 'lodash/isEqual';
import find from 'lodash/find';
import {
isBreakpoint,
createObject,

View File

@ -1,7 +1,7 @@
// 主题管理
import cx from 'classnames';
import React from 'react';
import hoistNonReactStatic = require('hoist-non-react-statics');
import hoistNonReactStatic from 'hoist-non-react-statics';
import {ExtractProps, Omit} from './types';
export type ClassValue =

View File

@ -14,6 +14,7 @@ export interface ApiObject {
cache?: number;
qsOptions?: any;
dataType?: 'json' | 'form-data' | 'form';
replaceData?: boolean;
}
export type ApiString = string;
export type Api = ApiString | ApiObject;

View File

@ -1,5 +1,5 @@
import find = require('lodash/find');
import findIndex = require('lodash/findIndex');
import find from 'lodash/find';
import findIndex from 'lodash/findIndex';
export class SimpleMap<V = any, K = any> {
private readonly list: Array<{

View File

@ -1,11 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import hoistNonReactStatic = require('hoist-non-react-statics');
import domOwnerDocument = require('dom-helpers/ownerDocument');
import css = require('dom-helpers/style/index');
import getOffset = require('dom-helpers/query/offset');
import getPosition = require('dom-helpers/query/position');
import getScrollTop = require('dom-helpers/query/scrollTop');
import hoistNonReactStatic from 'hoist-non-react-statics';
import domOwnerDocument from 'dom-helpers/ownerDocument';
import css from 'dom-helpers/style/index';
import getOffset from 'dom-helpers/query/offset';
import getPosition from 'dom-helpers/query/position';
import getScrollTop from 'dom-helpers/query/scrollTop';
const bsMapping: {
[propName: string]: string;
@ -198,8 +198,8 @@ export function calculatePosition(
// 如果还有其他可选项,则做位置判断,是否在可视区域,不完全在则继续看其他定位情况。
if (tests.length) {
const transformed = {
x: clip.x + positionLeft / scale - childOffset.left,
y: clip.y + positionTop / scale - childOffset.top,
x: clip.x + positionLeft / scale,
y: clip.y + positionTop / scale,
width: overlayWidth,
height: overlayHeight
};

View File

@ -1,8 +1,8 @@
import isPlainObject = require('lodash/isPlainObject');
import transform = require('lodash/transform');
import isEqual = require('lodash/isEqual');
import lodashIsObject = require('lodash/isObject');
import uniq = require('lodash/uniq');
import isPlainObject from 'lodash/isPlainObject';
import transform from 'lodash/transform';
import isEqual from 'lodash/isEqual';
import lodashIsObject from 'lodash/isObject';
import uniq from 'lodash/uniq';
import {Schema, PlainObject, FunctionPropertyNames} from '../types';
import {evalExpression} from './tpl';
import {boundMethod} from 'autobind-decorator';
@ -38,24 +38,30 @@ export function createObject(
return obj;
}
export function cloneObject(from: any) {
export function cloneObject(target: any, persistOwnProps: boolean = true) {
const obj =
from && from.__super
? Object.create(from.__super, {
target && target.__super
? Object.create(target.__super, {
__super: {
value: from.__super,
value: target.__super,
writable: false,
enumerable: false
}
})
: Object.create(Object.prototype);
from && Object.keys(from).forEach(key => (obj[key] = from[key]));
persistOwnProps &&
target &&
Object.keys(target).forEach(key => (obj[key] = target[key]));
return obj;
}
export function extendObject(to: any, from?: any) {
const obj = cloneObject(to);
from && Object.keys(from).forEach(key => (obj[key] = from[key]));
export function extendObject(
target: any,
src?: any,
persistOwnProps: boolean = true
) {
const obj = cloneObject(target, persistOwnProps);
src && Object.keys(src).forEach(key => (obj[key] = src[key]));
return obj;
}

View File

@ -1,7 +1,7 @@
import {reigsterTplEnginer, filter} from './tpl';
import moment from 'moment';
import {PlainObject} from '../types';
import isPlainObject = require('lodash/isPlainObject');
import isPlainObject from 'lodash/isPlainObject';
import {createObject, isObject, setVariable, qsstringify} from './helper';
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
@ -244,10 +244,6 @@ export const filters: {
? RegExp.$2
: resolveVariable(arg1, this as any)
: '';
// 比对的值是空时直接返回。
if (!arg1) {
return input;
}
fn = value => arg1 == value;
} else {
if (directive !== 'match') {

View File

@ -1,5 +1,5 @@
import {reigsterTplEnginer, filter} from './tpl';
import template = require('lodash/template');
import template from 'lodash/template';
import {getFilters} from './tpl-builtin';
import React from 'react';
import moment from 'moment';