mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-04 12:58:38 +08:00
Merge branch 'baidu:master' into master
This commit is contained in:
commit
c8fb3f936e
@ -207,6 +207,7 @@ order: 27
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| -------------------- | ----------------------------------------- | --------- | -------------------------------------------------------- |
|
||||
| type | `string` | `"alert"` | 指定为 alert 渲染器 |
|
||||
| title | `string` | | alert标题 |
|
||||
| className | `string` | | 外层 Dom 的类名 |
|
||||
| level | `string` | `info` | 级别,可以是:`info`、`success`、`warning` 或者 `danger` |
|
||||
| body | [SchemaNode](../../docs/types/schemanode) | | 显示内容 |
|
||||
|
@ -15,7 +15,7 @@ order: 27
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "avatar",
|
||||
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||
"src": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
@ -44,6 +44,7 @@ order: 27
|
||||
## 动态图片或文字
|
||||
|
||||
src、text 都支持变量,可以从上下文中动态获取图片或文字,下面的例子中:
|
||||
|
||||
- 第一个获取到了,显示正常
|
||||
- 第二个没获取到,因此降级为显示 icon
|
||||
- 第三个图片没获取到,由于 text 优先级比 icon 高,所以显示 text
|
||||
@ -51,7 +52,7 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
||||
```schema
|
||||
{
|
||||
"data": {
|
||||
"myAvatar": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||
"myAvatar": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
"type": "page",
|
||||
"body": [
|
||||
@ -122,16 +123,16 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
||||
{
|
||||
"type": "avatar",
|
||||
"size": 60,
|
||||
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||
"src": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"type": "avatar",
|
||||
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||
"src": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"type": "avatar",
|
||||
"size": 20,
|
||||
"src": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg"
|
||||
"src": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
]
|
||||
|
||||
@ -213,6 +214,7 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
||||
```
|
||||
|
||||
## 图片加载失败后,通过 onError 控制是否进行 text、icon 置换
|
||||
|
||||
> 如果同时存在 text 和 icon,会优先用 text、接着 icon
|
||||
|
||||
```schema: scope="body"
|
||||
@ -241,18 +243,18 @@ src、text 都支持变量,可以从上下文中动态获取图片或文字,
|
||||
|
||||
## 属性表
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --------- | ----------- | ------ | --------------------- |
|
||||
| className | `string` | | 外层 dom 的类名 |
|
||||
| style | `object` | | 外层 dom 的样式 |
|
||||
| fit |`'contain'` \| `'cover'` \| `'fill'` \| `'none'` \| `'scale-down'` | `'cover'` | 具体细节可以参考 MDN [文档](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit) |
|
||||
| src | `string` | | 图片地址 |
|
||||
| text | `string` | | 文字 |
|
||||
| icon | `string` | `'fa fa-user'` | 图标 |
|
||||
| shape | `'circle'` \| `'square'` \| `'rounded'` | `'circle'` | 形状,有三种 `'circle'` (圆形)、`'square'`(正方形)、`'rounded'`(圆角) |
|
||||
| size | `number` \| `'default'` \| `'normal'` \| `'small'` | `'default'` | `'default' \| 'normal' \| 'small'`三种字符串类型代表不同大小(分别是48、40、32),也可以直接数字表示 |
|
||||
| gap | `number` | 4 | 控制字符类型距离左右两侧边界单位像素 |
|
||||
| alt | `number` | | 图像无法显示时的替代文本 |
|
||||
| draggable | `boolean` | | 图片是否允许拖动 |
|
||||
| crossOrigin | `'anonymous'` \| `'use-credentials'` \| `''` | | 图片的 `CORS` 属性设置 |
|
||||
| onError | `string` | | 图片加载失败的字符串,这个字符串是一个New Function内部执行的字符串,参数是event(使用event.nativeEvent获取原生dom事件),这个字符串需要返回boolean值。设置 `"return ture;"` 会在图片加载失败后,使用 `text` 或者 `icon` 代表的信息来进行替换。目前图片加载失败默认是不进行置换。注意:图片加载失败,不包括$获取数据为空情况 |
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ----------- | ------------------------------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| className | `string` | | 外层 dom 的类名 |
|
||||
| style | `object` | | 外层 dom 的样式 |
|
||||
| fit | `'contain'` \| `'cover'` \| `'fill'` \| `'none'` \| `'scale-down'` | `'cover'` | 具体细节可以参考 MDN [文档](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit) |
|
||||
| src | `string` | | 图片地址 |
|
||||
| text | `string` | | 文字 |
|
||||
| icon | `string` | `'fa fa-user'` | 图标 |
|
||||
| shape | `'circle'` \| `'square'` \| `'rounded'` | `'circle'` | 形状,有三种 `'circle'` (圆形)、`'square'`(正方形)、`'rounded'`(圆角) |
|
||||
| size | `number` \| `'default'` \| `'normal'` \| `'small'` | `'default'` | `'default' \| 'normal' \| 'small'`三种字符串类型代表不同大小(分别是 48、40、32),也可以直接数字表示 |
|
||||
| gap | `number` | 4 | 控制字符类型距离左右两侧边界单位像素 |
|
||||
| alt | `number` | | 图像无法显示时的替代文本 |
|
||||
| draggable | `boolean` | | 图片是否允许拖动 |
|
||||
| crossOrigin | `'anonymous'` \| `'use-credentials'` \| `''` | | 图片的 `CORS` 属性设置 |
|
||||
| onError | `string` | | 图片加载失败的字符串,这个字符串是一个 New Function 内部执行的字符串,参数是 event(使用 event.nativeEvent 获取原生 dom 事件),这个字符串需要返回 boolean 值。设置 `"return ture;"` 会在图片加载失败后,使用 `text` 或者 `icon` 代表的信息来进行替换。目前图片加载失败默认是不进行置换。注意:图片加载失败,不包括$获取数据为空情况 |
|
||||
|
@ -26,7 +26,7 @@ order: 33
|
||||
},
|
||||
{
|
||||
"thumbMode": "contain",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -187,7 +187,7 @@ itemSchema: {
|
||||
},
|
||||
{
|
||||
"thumbMode": "contain",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
],
|
||||
"onEvent": {
|
||||
@ -250,7 +250,7 @@ itemSchema: {
|
||||
},
|
||||
{
|
||||
"thumbMode": "contain",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -292,7 +292,7 @@ itemSchema: {
|
||||
},
|
||||
{
|
||||
"thumbMode": "contain",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -337,7 +337,7 @@ itemSchema: {
|
||||
},
|
||||
{
|
||||
"thumbMode": "contain",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ order: 43
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -400,6 +400,30 @@ order: 43
|
||||
}
|
||||
```
|
||||
|
||||
> 3.3.0 及以上版本
|
||||
|
||||
如果是表单,可以在表单上配置 `close: false`
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "button",
|
||||
"label": "弹个框",
|
||||
"actionType": "drawer",
|
||||
"drawer": {
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "name",
|
||||
"label": "姓名"
|
||||
}
|
||||
],
|
||||
"close": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 配置弹窗的按钮
|
||||
|
||||
默认弹窗会自动生成两个按钮,一个取消,一个确认。如果通过 `actions` 来自定义配置,则以配置的为准。
|
||||
@ -445,8 +469,8 @@ order: 43
|
||||
| title | [SchemaNode](../../docs/types/schemanode) | | 弹出层标题 |
|
||||
| body | [SchemaNode](../../docs/types/schemanode) | | 往 Drawer 内容区加内容 |
|
||||
| size | `string` | | 指定 Drawer 大小,支持: `xs`、`sm`、`md`、`lg`、`xl` |
|
||||
| position | `string` | | 指定 Drawer 方向,支持: `left`、`right`、`top`、`bottom` |
|
||||
| className | `string` | `` | Drawer 最外层容器的样式类名 |
|
||||
| position | `string` | `right` | 指定 Drawer 方向,支持: `left`、`right`、`top`、`bottom` |
|
||||
| className | `string` | | Drawer 最外层容器的样式类名 |
|
||||
| headerClassName | `string` | | Drawer 头部 区域的样式类名 |
|
||||
| bodyClassName | `string` | `modal-body` | Drawer body 区域的样式类名 |
|
||||
| footerClassName | `string` | | Drawer 页脚 区域的样式类名 |
|
||||
|
@ -647,14 +647,12 @@ Form 默认会在底部渲染一个提交按钮,用于执行表单的提交行
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"type": 'button-toolbar',
|
||||
"name": 'button-toolbar',
|
||||
"type": "button-toolbar",
|
||||
"buttons": [
|
||||
{
|
||||
"type": "button",
|
||||
"label": "提交",
|
||||
"label": "${isStatic ? '编辑' : '提交'}",
|
||||
"level": "primary",
|
||||
"visibleOn": "${!isStatic}",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
@ -663,45 +661,26 @@ Form 默认会在底部渲染一个提交按钮,用于执行表单的提交行
|
||||
"componentId": "allFormSwitch",
|
||||
"args": {
|
||||
"value": {
|
||||
"isStatic": true
|
||||
"isStatic": "${!isStatic}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"actionType": "static",
|
||||
"componentId": "allFormSwitch"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"label": "编辑",
|
||||
"level": "primary",
|
||||
"visibleOn": "${isStatic}",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "setValue",
|
||||
"componentId": "allFormSwitch",
|
||||
"args": {
|
||||
"value": {
|
||||
"isStatic": false
|
||||
}
|
||||
}
|
||||
"expression": "${!isStatic}"
|
||||
},
|
||||
{
|
||||
"actionType": "nonstatic",
|
||||
"componentId": "allFormSwitch"
|
||||
"componentId": "allFormSwitch",
|
||||
"expression": "${isStatic}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
],
|
||||
"actions": []
|
||||
}
|
||||
|
@ -71,6 +71,27 @@ order: 10
|
||||
}
|
||||
```
|
||||
|
||||
## 配置下拉框样式
|
||||
|
||||
可以通过 `itemClassName` 指定下拉框样式,如配置最小宽度
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"name": "city",
|
||||
"type": "input-city",
|
||||
"label": "城市",
|
||||
"itemClassName": "min-w-xs",
|
||||
"searchable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
@ -210,6 +210,26 @@ order: 15
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认日期范围存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-date-range",
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "日期范围"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
@ -228,6 +248,7 @@ order: 15
|
||||
| clearable | `boolean` | `true` | 是否可清除 |
|
||||
| embed | `boolean` | `false` | 是否内联模式 |
|
||||
| animation | `boolean` | `true` | 是否启用游标动画 | `2.2.0` |
|
||||
| extraName | `string` | | 是否存成两个字段 | `3.3.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -102,6 +102,26 @@ order: 16
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认日期范围存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-datetime-range",
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "日期范围"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
@ -117,6 +137,7 @@ order: 16
|
||||
| utc | `boolean` | `false` | [保存 UTC 值](./input-datetime#utc) |
|
||||
| clearable | `boolean` | `true` | 是否可清除 |
|
||||
| animation | `boolean` | `true` | 是否启用游标动画 | `2.2.0` |
|
||||
| extraName | `string` | | 是否存成两个字段 | `3.3.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -43,6 +43,26 @@ order: 15
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认月份范围存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-month-range",
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "月份范围"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
@ -60,6 +80,7 @@ order: 15
|
||||
| clearable | `boolean` | `true` | 是否可清除 |
|
||||
| embed | `boolean` | `false` | 是否内联模式 |
|
||||
| animation | `boolean` | `true` | 是否启用游标动画 | `2.2.0` |
|
||||
| extraName | `string` | | 是否存成两个字段 | `3.3.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -26,7 +26,7 @@ order: 32
|
||||
|
||||
## 设置精度
|
||||
|
||||
`precision` 设置数字的显示精度,一般需要配合`step`属性使用,以实现细粒度调整。注意带有单位的输入不支持配置精度属性。若设置了`step`值,则会基于`step` 和`precision`的值,选择更高的精度。若输入的内容不满足精度要求,组件会按照精度自动处理,遵循四舍五入规则。
|
||||
`precision` 设置数字的显示精度,一般需要配合 `step` 属性使用,以实现细粒度调整。注意带有单位的输入不支持配置精度属性。若设置了 `step` 值,则会基于 `step` 和 `precision` 的值,选择更高的精度。若输入的内容不满足精度要求,组件会按照精度自动处理,遵循四舍五入规则。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -65,7 +65,7 @@ order: 32
|
||||
|
||||
## 重置值
|
||||
|
||||
清空/重置组件输入后,组件绑定的值将被设置为`resetValue`,默认为`""`。若`resetValue`为合法数字时,会根据`min`、`max`和`precision`属性,将组件值设置为满足条件的值。若`resetValue`为非数字,则组件清空/重置后设置为该值。
|
||||
清空/重置组件输入后,组件绑定的值将被设置为 `resetValue`,默认为 `""`。若 `resetValue` 为合法数字时,会根据 `min`、`max` 和 `precision` 属性,将组件值设置为满足条件的值。若 `resetValue` 为非数字,则组件清空/重置后设置为该值。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -93,7 +93,7 @@ order: 32
|
||||
"type": "input-number",
|
||||
"name": "number3",
|
||||
"label": "数字带有max和precision",
|
||||
"max": 100.50,
|
||||
"max": 100.5,
|
||||
"precision": 2,
|
||||
"resetValue": 1000,
|
||||
"value": 1234,
|
||||
@ -174,7 +174,7 @@ order: 32
|
||||
|
||||
> 2.3.0 及以上版本
|
||||
|
||||
默认情况下使用 JavaScript 原生数字类型,但如果要支持输入超过 JavaScript 支持范围的整数或浮点数,可以通过 `"big": true` 开启大数支持,开启之后输入输出都将是字符串
|
||||
默认情况下使用 JavaScript 原生数字类型,但如果要支持输入超过 JavaScript 支持范围的整数或浮点数,可以通过 `"big": true` 开启大数支持,开启之后输入输出都将是字符串。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -186,7 +186,7 @@ order: 32
|
||||
"type": "input-number",
|
||||
"name": "number",
|
||||
"label": "数字",
|
||||
"big": "true"
|
||||
"big": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -196,65 +196,65 @@ order: 32
|
||||
|
||||
> 2.8.0 及以上版本
|
||||
|
||||
如果设置了`"clearValueOnEmpty": true`,当输入框的值清空时,会从数据域中删除该表单项对应的值。比较常见的用法是在`combo`,`input-array`等组件中避免`input-number`清空后提交空字符串。
|
||||
如果设置了 `"clearValueOnEmpty": true`,当输入框的值清空时,会从数据域中删除该表单项对应的值。比较常见的用法是在 `combo`、`input-array` 等组件中避免 `input-number` 清空后提交空字符串。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"debugConfig": {
|
||||
"levelExpand": 2
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "group",
|
||||
"body": [
|
||||
{
|
||||
"name": "numberClear",
|
||||
"type": "input-number",
|
||||
"label": "清空",
|
||||
"value": 123,
|
||||
"clearValueOnEmpty": true
|
||||
},
|
||||
{
|
||||
"name": "numberNotClear",
|
||||
"type": "input-number",
|
||||
"label": "不清空",
|
||||
"value": 456
|
||||
}
|
||||
]
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"debugConfig": {
|
||||
"levelExpand": 2
|
||||
},
|
||||
{
|
||||
"type": "combo",
|
||||
"name": "user",
|
||||
"label": "用户",
|
||||
"items": [
|
||||
"body": [
|
||||
{
|
||||
"name": "text",
|
||||
"label": "名字",
|
||||
"type": "input-text"
|
||||
"type": "group",
|
||||
"body": [
|
||||
{
|
||||
"name": "numberClear",
|
||||
"type": "input-number",
|
||||
"label": "清空",
|
||||
"value": 123,
|
||||
"clearValueOnEmpty": true
|
||||
},
|
||||
{
|
||||
"name": "numberNotClear",
|
||||
"type": "input-number",
|
||||
"label": "不清空",
|
||||
"value": 456
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gender",
|
||||
"label": "性别",
|
||||
"type": "select",
|
||||
"options": ["男", "女"]
|
||||
},
|
||||
{
|
||||
"name": "age",
|
||||
"label": "年龄",
|
||||
"type": "input-number",
|
||||
"clearValueOnEmpty": true
|
||||
"type": "combo",
|
||||
"name": "user",
|
||||
"label": "用户",
|
||||
"items": [
|
||||
{
|
||||
"name": "text",
|
||||
"label": "名字",
|
||||
"type": "input-text"
|
||||
},
|
||||
{
|
||||
"name": "gender",
|
||||
"label": "性别",
|
||||
"type": "select",
|
||||
"options": ["男", "女"]
|
||||
},
|
||||
{
|
||||
"name": "age",
|
||||
"label": "年龄",
|
||||
"type": "input-number",
|
||||
"clearValueOnEmpty": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 原生数字组件
|
||||
|
||||
原生数字组件将直接使用浏览器的实现,最终展现效果和浏览器有关,而且只支持 `min`、`max`、`step` 这几个属性设置。
|
||||
原生数字组件将直接使用浏览器的实现,最终展现效果和浏览器有关,并且只支持 `min`、`max` 和 `step` 这几个属性设置。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -274,27 +274,30 @@ order: 32
|
||||
|
||||
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ----------------- | --------------------------------------- | ------- | ------------------------------------------ | ------- |
|
||||
| min | [模板](../../../docs/concepts/template) | | 最小值 |
|
||||
| max | [模板](../../../docs/concepts/template) | | 最大值 |
|
||||
| step | `number` | | 步长 |
|
||||
| precision | `number` | | 精度,即小数点后几位,支持 0 和正整数 |
|
||||
| showSteps | `boolean` | | 是否显示上下点击按钮 |
|
||||
| prefix | `string` | | 前缀 |
|
||||
| suffix | `string` | | 后缀 |
|
||||
| kilobitSeparator | `boolean` | | 千分分隔 |
|
||||
| keyboard | `boolean` | | 键盘事件(方向上下) |
|
||||
| big | `boolean` | | 是否使用大数 |
|
||||
| displayMode | `string` | | 样式类型 |
|
||||
| resetValue | `number \| string` | `""` | 清空输入内容时,组件值将设置为`resetValue` |
|
||||
| clearValueOnEmpty | `boolean` | `false` | 内容为空时从数据域中删除该表单项对应的值 | `2.8.0` |
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ----------------- | --------------------------------------- | -------- | ------------------------------------------- | ------- |
|
||||
| min | [模板](../../../docs/concepts/template) | | 最小值 |
|
||||
| max | [模板](../../../docs/concepts/template) | | 最大值 |
|
||||
| step | `number` | | 步长 |
|
||||
| precision | `number` | | 精度,即小数点后几位,支持 0 和正整数 |
|
||||
| showSteps | `boolean` | `true` | 是否显示上下点击按钮 |
|
||||
| readOnly | `boolean` | `false` | 只读 |
|
||||
| prefix | `string` | | 前缀 |
|
||||
| suffix | `string` | | 后缀 |
|
||||
| unitOptions | `string[]` | | 单位选项 | `1.4.0` |
|
||||
| kilobitSeparator | `boolean` | `false` | 千分分隔 |
|
||||
| keyboard | `boolean` | `true` | 键盘事件(方向上下) |
|
||||
| big | `boolean` | `false` | 是否使用大数 | `2.3.0` |
|
||||
| displayMode | `"base" \| "enhance"` | `"base"` | 样式类型 |
|
||||
| borderMode | `"full" \| "half" \| "none"` | `"full"` | 边框模式,全边框,还是半边框,或者没边框 |
|
||||
| resetValue | `number \| string` | `""` | 清空输入内容时,组件值将设置为 `resetValue` |
|
||||
| clearValueOnEmpty | `boolean` | `false` | 内容为空时从数据域中删除该表单项对应的值 | `2.8.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
当前组件会对外派发以下事件,可以通过`onEvent`来监听这些事件,并通过`actions`来配置执行的动作,在`actions`中可以通过`${事件参数名}`或`${event.data.[事件参数名]}`来获取事件产生的数据,详细请查看[事件动作](../../docs/concepts/event-action)。
|
||||
当前组件会对外派发以下事件,可以通过 `onEvent` 来监听这些事件,并通过 `actions` 来配置执行的动作,在 `actions` 中可以通过 `${事件参数名}` 或 `${event.data.[事件参数名]}` 来获取事件产生的数据,详细请查看[事件动作](../../docs/concepts/event-action)。
|
||||
|
||||
> `[name]`表示当前组件绑定的名称,即`name`属性,如果没有配置`name`属性,则通过`value`取值。
|
||||
> `[name]` 表示当前组件绑定的名称,即 `name` 属性,如果没有配置 `name` 属性,则通过 `value` 取值。
|
||||
|
||||
| 事件名称 | 事件参数 | 说明 |
|
||||
| -------- | ------------------------- | ---------------- |
|
||||
@ -304,10 +307,10 @@ order: 32
|
||||
|
||||
## 动作表
|
||||
|
||||
当前组件对外暴露以下特性动作,其他组件可以通过指定`actionType: 动作名称`、`componentId: 该组件id`来触发这些动作,动作配置可以通过`args: {动作配置项名称: xxx}`来配置具体的参数,详细请查看[事件动作](../../docs/concepts/event-action#触发其他组件的动作)。
|
||||
当前组件对外暴露以下特性动作,其他组件可以通过指定 `actionType: 动作名称`、`componentId: 该组件id` 来触发这些动作,动作配置可以通过 `args: {动作配置项名称: xxx}` 来配置具体的参数,详细请查看[事件动作](../../docs/concepts/event-action#触发其他组件的动作)。
|
||||
|
||||
| 动作名称 | 动作配置 | 说明 |
|
||||
| -------- | -------------------------- | ------------------------------------------------------ |
|
||||
| clear | - | 清空 |
|
||||
| reset | - | 将值重置为`resetValue`,若没有配置`resetValue`,则清空 |
|
||||
| setValue | `value: number` 更新的数值 | 更新数据 |
|
||||
| 动作名称 | 动作配置 | 说明 |
|
||||
| -------- | -------------------------- | -------------------------------------------------------- |
|
||||
| clear | - | 清空 |
|
||||
| reset | - | 将值重置为 `resetValue`,若没有配置 `resetValue`,则清空 |
|
||||
| setValue | `value: number` 更新的数值 | 更新数据 |
|
||||
|
@ -42,6 +42,26 @@ order: 15
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认季度范围存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-quarter-range",
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "季度范围"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
@ -59,6 +79,7 @@ order: 15
|
||||
| clearable | `boolean` | `true` | 是否可清除 |
|
||||
| embed | `boolean` | `false` | 是否内联模式 |
|
||||
| animation | `boolean` | `true` | 是否启用游标动画 | `2.2.0` |
|
||||
| extraName | `string` | | 是否存成两个字段 | `3.3.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -251,6 +251,27 @@ order: 38
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认滑块多选存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-range",
|
||||
"multiple": true,
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "range"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
当做选择器表单项使用时,除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
@ -65,6 +65,26 @@ order: 15
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认季度范围存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-time-range",
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "时间范围"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
@ -78,6 +98,7 @@ order: 15
|
||||
| clearable | `boolean` | `true` | 是否可清除 |
|
||||
| embed | `boolean` | `false` | 是否内联模式 |
|
||||
| animation | `boolean` | `true` | 是否启用游标动画 | `2.2.0` |
|
||||
| extraName | `string` | | 是否存成两个字段 | `3.3.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -43,6 +43,26 @@ order: 15
|
||||
}
|
||||
```
|
||||
|
||||
## 存成两个字段
|
||||
|
||||
默认年份范围存储一个字段,用 `delemiter` 分割,如果配置 `extraName` 则会存两个字段。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-year-range",
|
||||
"name": "begin",
|
||||
"extraName": "end",
|
||||
"label": "年份范围"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
@ -52,22 +52,22 @@ ListSelect 一般用来实现选择,可以单选也可以多选,和 Radio/Ch
|
||||
{
|
||||
"label": "OptionA",
|
||||
"value": "a",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"label": "OptionB",
|
||||
"value": "b",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"label": "OptionC",
|
||||
"value": "c",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"label": "OptionD",
|
||||
"value": "d",
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ order: 52
|
||||
"type": "form",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg",
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg",
|
||||
"images": [
|
||||
{
|
||||
"image": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
|
||||
|
@ -609,10 +609,71 @@ icon:
|
||||
|
||||
设置这个 api,可以实现左侧选项搜索结果的检索。
|
||||
|
||||
##### 发送
|
||||
|
||||
默认 GET,携带 term 变量,值为搜索框输入的文字,可从上下文中取数据设置进去。
|
||||
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "page",
|
||||
"body": {
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"label": "searchApi",
|
||||
"type": "transfer",
|
||||
"name": "transfer",
|
||||
"searchable": true,
|
||||
"selectMode": "tree",
|
||||
"searchApi": "/api/transfer/search?name=${term}",
|
||||
"options": [
|
||||
{
|
||||
"label": "法师",
|
||||
"children": [
|
||||
{
|
||||
"label": "诸葛亮",
|
||||
"value": "zhugeliang"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "战士",
|
||||
"value": "zhanshi",
|
||||
"children": [
|
||||
{
|
||||
"label": "曹操",
|
||||
"value": "caocao"
|
||||
},
|
||||
{
|
||||
"label": "钟无艳",
|
||||
"value": "zhongwuyan"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "打野",
|
||||
"children": [
|
||||
{
|
||||
"label": "李白",
|
||||
"value": "libai"
|
||||
},
|
||||
{
|
||||
"label": "韩信",
|
||||
"value": "hanxin"
|
||||
},
|
||||
{
|
||||
"label": "云中君",
|
||||
"value": "yunzhongjun"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### 响应
|
||||
|
||||
格式要求如下:
|
||||
@ -625,7 +686,7 @@ icon:
|
||||
"options": [
|
||||
{
|
||||
"label": "描述",
|
||||
"value": "值" // ,
|
||||
"value": "值"
|
||||
// "children": [] // 可以嵌套
|
||||
},
|
||||
|
||||
@ -634,14 +695,13 @@ icon:
|
||||
"value": "值2"
|
||||
}
|
||||
],
|
||||
|
||||
"value": "值" // 默认值,可以获取列表的同时设置默认值。
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
适用于需选择的数据/信息源较多时,用户可直观的知道自己所选择的数据/信息的场景,一般左侧框为数据/信息源,右侧为已选数据/信息,被选中信息同时存在于 2 个框内。
|
||||
|
||||
|
||||
### 结果面板跟随模式
|
||||
|
||||
`resultListModeFollowSelect` 开启结果面板跟随模式。
|
||||
|
@ -259,7 +259,7 @@ Word 渲染支持以下功能:
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "img",
|
||||
"value": "https://suda.cdn.bcebos.com/images/amis/ai-fake-face.jpg",
|
||||
"value": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg",
|
||||
"label": "图片地址"
|
||||
},
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ order: 67
|
||||
"audio": "https://news-bos.cdn.bcebos.com/mvideo/%E7%9A%87%E5%90%8E%E5%A4%A7%E9%81%93%E4%B8%9C.aac",
|
||||
"carousel": [
|
||||
{
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"html": "<div style=\"width: 100%; height: 200px; background: #e3e3e3; text-align: center; line-height: 200px;\">carousel data in crud</div>"
|
||||
@ -128,7 +128,7 @@ order: 67
|
||||
}
|
||||
],
|
||||
"date": 1591270438,
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg",
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg",
|
||||
"json": {
|
||||
"id": 1,
|
||||
"text": "text"
|
||||
@ -185,7 +185,7 @@ order: 67
|
||||
"audio": "https://news-bos.cdn.bcebos.com/mvideo/%E7%9A%87%E5%90%8E%E5%A4%A7%E9%81%93%E4%B8%9C.aac",
|
||||
"carousel": [
|
||||
{
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"html": "<div style=\"width: 100%; height: 200px; background: #e3e3e3; text-align: center; line-height: 200px;\">carousel data in crud</div>"
|
||||
@ -195,7 +195,7 @@ order: 67
|
||||
}
|
||||
],
|
||||
"date": 1591270438,
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg",
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg",
|
||||
"json": {
|
||||
"id": 1,
|
||||
"text": "text"
|
||||
@ -244,7 +244,7 @@ order: 67
|
||||
"audio": "https://news-bos.cdn.bcebos.com/mvideo/%E7%9A%87%E5%90%8E%E5%A4%A7%E9%81%93%E4%B8%9C.aac",
|
||||
"carousel": [
|
||||
{
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
},
|
||||
{
|
||||
"html": "<div style=\"width: 100%; height: 200px; background: #e3e3e3; text-align: center; line-height: 200px;\">carousel data in crud</div>"
|
||||
@ -254,7 +254,7 @@ order: 67
|
||||
}
|
||||
],
|
||||
"date": 1591270438,
|
||||
"image": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg",
|
||||
"image": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg",
|
||||
"json": {
|
||||
"id": 1,
|
||||
"text": "text"
|
||||
|
@ -2,11 +2,11 @@
|
||||
title: 如何贡献代码
|
||||
---
|
||||
|
||||
如果发现 amis 有不满足的功能,除了发 issue 等官方升级之外,最快的方法就是自己实现它,本文将介绍 amis 代码的基本结构,一步步教会你如何新增功能。
|
||||
如果发现 amis 有不满足的功能,除了发 issue 等官方升级之外,最快的方法就是自己实现它,本文将介绍 amis 代码的基本结构,并一步步教你如何新增功能。
|
||||
|
||||
## 准备开始
|
||||
|
||||
1. 首先,你需要对 React 有基本了解,快速看一遍[官方文档](https://zh-hans.reactjs.org/docs/getting-started.html)就行。
|
||||
1. 首先,你需要对 React 有基本了解,快速看一遍[官方文档](https://zh-hans.react.dev/learn)即可。
|
||||
2. 在 github 上 fork amis 项目到自己的账号下。
|
||||
3. 创建分支 `git checkout -b feat-xxx`
|
||||
|
||||
|
@ -155,6 +155,8 @@ let amisScoped = amis.embed(
|
||||
// 另外在 amis 配置项中的 api 也可以配置适配器,针对某个特定接口单独处理。
|
||||
//
|
||||
// requestAdaptor(api) {
|
||||
// // 支持异步,可以通过 api.mockResponse 来设置返回结果,跳过真正的请求发送
|
||||
// // 此功能自定义 fetcher 的话会失效
|
||||
// return api;
|
||||
// }
|
||||
//
|
||||
|
@ -659,6 +659,7 @@ const schema = {
|
||||
url: '/api/mock2/form/saveForm',
|
||||
requestAdaptor: function (api, context) {
|
||||
console.log(context); // 打印上下文数据
|
||||
|
||||
return {
|
||||
...api,
|
||||
data: {
|
||||
@ -687,6 +688,47 @@ const schema = {
|
||||
|
||||
你也可以使用`debugger`自行进行调试。
|
||||
|
||||
#### 拦截请求
|
||||
|
||||
如果 api 发送适配器中,修改 api 对象,在 api 对象里面放入 `mockReponse` 属性,则会拦截请求发送,amis 内部会直接使用 `mockReponse` 的结果返回。
|
||||
|
||||
```js
|
||||
const schema = {
|
||||
type: 'form',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: '/api/mock2/form/saveForm',
|
||||
requestAdaptor: function (api, context) {
|
||||
return {
|
||||
// 模拟 http 请求返回
|
||||
mockResponse: {
|
||||
status: 200, // http 返回状态
|
||||
data: {
|
||||
// http 返回结果
|
||||
status: 0, // amis 返回数据的状态
|
||||
data: {
|
||||
name: '模拟返回的值'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '姓名:'
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
type: 'input-email',
|
||||
label: '邮箱:'
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### 配置接收适配器
|
||||
|
||||
同样的,如果后端返回的响应结构不符合 amis 的[接口格式要求](#%E6%8E%A5%E5%8F%A3%E8%BF%94%E5%9B%9E%E6%A0%BC%E5%BC%8F-%E9%87%8D%E8%A6%81-),而后端不方便调整时,可以配置`adaptor`实现接收适配器
|
||||
|
@ -74,8 +74,7 @@ export default {
|
||||
},
|
||||
{
|
||||
thumbMode: 'contain',
|
||||
image:
|
||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||
image: 'https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg'
|
||||
}
|
||||
],
|
||||
onEvent: {
|
||||
|
@ -51,12 +51,12 @@ export function embed(
|
||||
container.classList.add('amis-scope');
|
||||
let scoped = {};
|
||||
|
||||
const requestAdaptor = (config: any) => {
|
||||
const requestAdaptor = async (config: any) => {
|
||||
const fn =
|
||||
env && typeof env.requestAdaptor === 'function'
|
||||
? env.requestAdaptor.bind()
|
||||
: (config: any) => config;
|
||||
const request = fn(config) || config;
|
||||
: async (config: any) => config;
|
||||
const request = (await fn(config)) || config;
|
||||
|
||||
return request;
|
||||
};
|
||||
@ -189,7 +189,7 @@ export function embed(
|
||||
config.method = method;
|
||||
config.data = data;
|
||||
|
||||
config = requestAdaptor(config);
|
||||
config = await requestAdaptor(config);
|
||||
|
||||
if (method === 'get' && data) {
|
||||
config.params = data;
|
||||
@ -210,7 +210,9 @@ export function embed(
|
||||
return true;
|
||||
};
|
||||
|
||||
let response = await axios(config);
|
||||
let response = config.mockResponse
|
||||
? config.mockResponse
|
||||
: await axios(config);
|
||||
response = await attachmentAdpator(response, __);
|
||||
response = responseAdaptor(api)(response);
|
||||
|
||||
|
@ -33,8 +33,7 @@ module.exports = function (req, res) {
|
||||
'https://news-bos.cdn.bcebos.com/mvideo/%E7%9A%87%E5%90%8E%E5%A4%A7%E9%81%93%E4%B8%9C.aac',
|
||||
carousel: [
|
||||
{
|
||||
image:
|
||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||
image: 'https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg'
|
||||
},
|
||||
{
|
||||
html: '<div style="width: 100%; height: 200px; background: #e3e3e3; text-align: center; line-height: 200px;">carousel data in crud</div>'
|
||||
@ -45,8 +44,7 @@ module.exports = function (req, res) {
|
||||
}
|
||||
],
|
||||
date: Math.round(Date.now() / 1000),
|
||||
image:
|
||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||
image: 'https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg'
|
||||
}),
|
||||
parseInt(req.query.perPage, 10) || 10
|
||||
)
|
||||
|
@ -6,17 +6,15 @@ module.exports = function (req, res) {
|
||||
imageList: [
|
||||
{
|
||||
image:
|
||||
'https://internal-amis-res.cdn.bcebos.com/images/2019-12/1577157239810/da6376bf988c.png',
|
||||
'https://internal-amis-res.cdn.bcebos.com/images/2019-12/1577157239810/da6376bf988c.png'
|
||||
},
|
||||
{
|
||||
html:
|
||||
'<div style="width: 100%; height: 300px; background: #e3e3e3; text-align: center; line-height: 300px;">carousel data</div>',
|
||||
html: '<div style="width: 100%; height: 300px; background: #e3e3e3; text-align: center; line-height: 300px;">carousel data</div>'
|
||||
},
|
||||
{
|
||||
image:
|
||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg',
|
||||
},
|
||||
],
|
||||
},
|
||||
image: 'https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -32,8 +32,7 @@ module.exports = function (req, res) {
|
||||
Math.round(Math.random() * 10)
|
||||
),
|
||||
date: Math.round(Date.now() / 1000),
|
||||
image:
|
||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||
image: 'https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg'
|
||||
})),
|
||||
|
||||
table2: repeat(() => ({
|
||||
@ -50,8 +49,7 @@ module.exports = function (req, res) {
|
||||
Math.round(Math.random() * 10)
|
||||
),
|
||||
date: Math.round(Date.now() / 1000),
|
||||
image:
|
||||
'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg'
|
||||
image: 'https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg'
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
27
mock/cfc/mock/transfer/search.json
Normal file
27
mock/cfc/mock/transfer/search.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"status": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"options": [
|
||||
{
|
||||
"label": "法师",
|
||||
"children": [
|
||||
{
|
||||
"label": "诸葛亮",
|
||||
"value": "zhugeliang"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "战士",
|
||||
"value": "zhanshi",
|
||||
"children": [
|
||||
{
|
||||
"label": "钟无艳",
|
||||
"value": "zhongwuyan"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"status": 0,
|
||||
"msg": "ok",
|
||||
"link": "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3893101144,2877209892&fm=23&gp=0.jpg"
|
||||
}
|
||||
"link": "https://suda.cdn.bcebos.com/amis/images/alice-macaw.jpg"
|
||||
}
|
@ -61,7 +61,8 @@ export const RENDERER_TRANSMISSION_OMIT_PROPS = [
|
||||
'inputOnly',
|
||||
'label',
|
||||
'renderLabel',
|
||||
'trackExpression'
|
||||
'trackExpression',
|
||||
'editorSetting'
|
||||
];
|
||||
|
||||
const componentCache: SimpleMap = new SimpleMap();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import omit from 'lodash/omit';
|
||||
import {RendererProps} from '../factory';
|
||||
import {ConditionGroupValue} from '../types';
|
||||
import {ConditionGroupValue, Api, SchemaNode} from '../types';
|
||||
import {createObject} from '../utils/helper';
|
||||
import {RendererEvent} from '../utils/renderer-event';
|
||||
import {evalExpressionWithConditionBuilder} from '../utils/tpl';
|
||||
@ -32,6 +32,7 @@ export interface ListenerAction {
|
||||
stopPropagation?: boolean; // 阻止后续的事件处理器执行
|
||||
expression?: string | ConditionGroupValue; // 执行条件
|
||||
execOn?: string; // 执行条件,1.9.0废弃
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface ILogicAction extends ListenerAction {
|
||||
@ -158,12 +159,19 @@ export const runActions = async (
|
||||
!actionInstrance &&
|
||||
(actionConfig.componentId || actionConfig.componentName)
|
||||
) {
|
||||
actionInstrance = getActionByType('component');
|
||||
} else if (
|
||||
actionConfig.actionType === 'url' ||
|
||||
actionConfig.actionType === 'link' ||
|
||||
actionConfig.actionType === 'jump'
|
||||
) {
|
||||
actionInstrance = [
|
||||
'static',
|
||||
'nonstatic',
|
||||
'show',
|
||||
'visibility',
|
||||
'hidden',
|
||||
'enabled',
|
||||
'disabled',
|
||||
'usability'
|
||||
].includes(actionConfig.actionType)
|
||||
? getActionByType('status')
|
||||
: getActionByType('component');
|
||||
} else if (['url', 'link', 'jump'].includes(actionConfig.actionType)) {
|
||||
// 打开页面动作
|
||||
actionInstrance = getActionByType('openlink');
|
||||
}
|
||||
@ -175,6 +183,7 @@ export const runActions = async (
|
||||
|
||||
// 这些节点的子节点运行逻辑由节点内部实现
|
||||
await runAction(actionInstrance, actionConfig, renderer, event);
|
||||
|
||||
if (event.stoped) {
|
||||
break;
|
||||
}
|
||||
@ -192,6 +201,8 @@ export const runAction = async (
|
||||
let additional: any = {
|
||||
event
|
||||
};
|
||||
let action: ListenerAction = {...actionConfig};
|
||||
action.args = {...actionConfig.args};
|
||||
|
||||
// __rendererData默认为renderer.props.data,兼容表单项值变化时的data读取
|
||||
if (!event.data.__rendererData) {
|
||||
@ -214,7 +225,7 @@ export const runAction = async (
|
||||
event.data
|
||||
);
|
||||
// 兼容一下1.9.0之前的版本
|
||||
const expression = actionConfig.expression ?? actionConfig.execOn;
|
||||
const expression = action.expression ?? action.execOn;
|
||||
// 执行条件
|
||||
let isStop = false;
|
||||
|
||||
@ -232,21 +243,38 @@ export const runAction = async (
|
||||
|
||||
// 支持表达式 >=1.10.0
|
||||
let preventDefault = false;
|
||||
if (actionConfig.preventDefault) {
|
||||
if (action.preventDefault) {
|
||||
preventDefault = await evalExpressionWithConditionBuilder(
|
||||
actionConfig.preventDefault,
|
||||
action.preventDefault,
|
||||
mergeData,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
let key = {
|
||||
componentId: dataMapping(actionConfig.componentId, mergeData),
|
||||
componentName: dataMapping(actionConfig.componentName, mergeData)
|
||||
componentId: dataMapping(action.componentId, mergeData),
|
||||
componentName: dataMapping(action.componentName, mergeData)
|
||||
};
|
||||
|
||||
// 兼容args包裹的用法
|
||||
if (action.actionType === 'dialog') {
|
||||
action.dialog = {...(action.dialog ?? action.args?.dialog)};
|
||||
delete action.args?.dialog;
|
||||
} else if (action.actionType === 'drawer') {
|
||||
action.drawer = {...(action.drawer ?? action.args?.drawer)};
|
||||
delete action.args?.drawer;
|
||||
} else if (action.actionType === 'ajax') {
|
||||
const api = action.api ?? action.args?.api;
|
||||
action.api = typeof api === 'string' ? api : {...api};
|
||||
action.options = {...(action.options ?? action.args?.options)};
|
||||
action.messages = {...(action.messages ?? action.args?.messages)};
|
||||
delete action.args?.api;
|
||||
delete action.args?.options;
|
||||
delete action.args?.messages;
|
||||
}
|
||||
|
||||
// 动作配置
|
||||
const args = dataMapping(actionConfig.args, mergeData, key =>
|
||||
const args = dataMapping(action.args, mergeData, key =>
|
||||
[
|
||||
'adaptor',
|
||||
'responseAdaptor',
|
||||
@ -255,7 +283,7 @@ export const runAction = async (
|
||||
'condition'
|
||||
].includes(key)
|
||||
);
|
||||
const afterMappingData = dataMapping(actionConfig.data, mergeData);
|
||||
const afterMappingData = dataMapping(action.data, mergeData);
|
||||
|
||||
// 动作数据
|
||||
const actionData =
|
||||
@ -265,27 +293,22 @@ export const runAction = async (
|
||||
...args, // 兼容历史(动作配置与数据混在一起的情况)
|
||||
...(afterMappingData ?? {})
|
||||
},
|
||||
getOmitActionProp(actionConfig.actionType)
|
||||
getOmitActionProp(action.actionType)
|
||||
)
|
||||
: afterMappingData;
|
||||
|
||||
// 默认为事件数据
|
||||
const data =
|
||||
args && !Object.keys(args).length && actionConfig.data === undefined // 兼容历史
|
||||
? {}
|
||||
: actionData !== undefined
|
||||
? actionData
|
||||
: event.data;
|
||||
// 默认为当前数据域
|
||||
const data = actionData !== undefined ? actionData : mergeData;
|
||||
|
||||
console.group?.(`run action ${actionConfig.actionType}`);
|
||||
console.debug(`[${actionConfig.actionType}] action args, data`, args, data);
|
||||
console.group?.(`run action ${action.actionType}`);
|
||||
console.debug(`[${action.actionType}] action args, data`, args, data);
|
||||
|
||||
let stopped = false;
|
||||
const actionResult = await actionInstrance.run(
|
||||
{
|
||||
...actionConfig,
|
||||
...action,
|
||||
args,
|
||||
data: actionConfig.actionType === 'reload' ? actionData : data, // 如果是刷新动作,则只传action.data
|
||||
data: action.actionType === 'reload' ? actionData : data, // 如果是刷新动作,则只传action.data
|
||||
...key
|
||||
},
|
||||
renderer,
|
||||
@ -293,19 +316,19 @@ export const runAction = async (
|
||||
mergeData
|
||||
);
|
||||
// 二次确认弹窗如果取消,则终止后续动作
|
||||
if (actionConfig?.actionType === 'confirmDialog' && !actionResult) {
|
||||
if (action?.actionType === 'confirmDialog' && !actionResult) {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
let stopPropagation = false;
|
||||
if (actionConfig.stopPropagation) {
|
||||
if (action.stopPropagation) {
|
||||
stopPropagation = await evalExpressionWithConditionBuilder(
|
||||
actionConfig.stopPropagation,
|
||||
action.stopPropagation,
|
||||
mergeData,
|
||||
false
|
||||
);
|
||||
}
|
||||
console.debug(`[${actionConfig.actionType}] action end event`, event);
|
||||
console.debug(`[${action.actionType}] action end event`, event);
|
||||
console.groupEnd?.();
|
||||
|
||||
// 阻止原有动作执行
|
||||
|
@ -43,25 +43,19 @@ export class AjaxAction implements RendererAction {
|
||||
throw new Error('env.fetcher is required!');
|
||||
}
|
||||
if (this.fetcherType === 'download' && action.actionType === 'download') {
|
||||
// 兼容老的格式
|
||||
if ((action as any).args?.api) {
|
||||
(action as any).args.api.responseType = 'blob';
|
||||
}
|
||||
if ((action as any)?.api) {
|
||||
if ((action as any).api) {
|
||||
(action as any).api.responseType = 'blob';
|
||||
}
|
||||
}
|
||||
|
||||
const env = event.context.env;
|
||||
const silent = action?.options?.silent ?? action.args?.options?.silent;
|
||||
const messages =
|
||||
(action?.api as ApiObject)?.messages ??
|
||||
(action.args?.api as ApiObject)?.messages;
|
||||
const silent = action?.options?.silent;
|
||||
const messages = (action?.api as ApiObject)?.messages;
|
||||
try {
|
||||
const result = await env.fetcher(
|
||||
action?.api ?? action.args?.api,
|
||||
action?.api,
|
||||
action.data ?? {},
|
||||
action?.options ?? action.args?.options ?? {}
|
||||
action?.options ?? {}
|
||||
);
|
||||
const responseData =
|
||||
!isEmpty(result.data) || result.ok
|
||||
@ -84,13 +78,13 @@ export class AjaxAction implements RendererAction {
|
||||
if (!silent) {
|
||||
if (!result.ok) {
|
||||
throw new ServerError(
|
||||
messages?.failed ?? action.args?.messages?.failed ?? result.msg,
|
||||
messages?.failed ?? action.messages?.failed ?? result.msg,
|
||||
result
|
||||
);
|
||||
} else {
|
||||
const msg =
|
||||
messages?.success ??
|
||||
action.args?.messages?.success ??
|
||||
action.messages?.success ??
|
||||
result.msg ??
|
||||
result.defaultMsg;
|
||||
msg &&
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {RendererEvent} from '../utils/renderer-event';
|
||||
import {createObject, isEmpty} from '../utils/helper';
|
||||
import {createObject} from '../utils/helper';
|
||||
import {
|
||||
RendererAction,
|
||||
ListenerAction,
|
||||
@ -8,22 +8,11 @@ import {
|
||||
} from './Action';
|
||||
|
||||
export interface ICmptAction extends ListenerAction {
|
||||
actionType:
|
||||
| 'setValue'
|
||||
| 'static'
|
||||
| 'nonstatic'
|
||||
| 'show'
|
||||
| 'visibility'
|
||||
| 'hidden'
|
||||
| 'enabled'
|
||||
| 'disabled'
|
||||
| 'usability'
|
||||
| 'reload';
|
||||
actionType: string;
|
||||
args: {
|
||||
/** actionType为setValue时,目标变量的path */
|
||||
path?: string;
|
||||
value?: string | {[key: string]: string};
|
||||
index?: number; // setValue支持更新指定索引的数据,一般用于数组类型
|
||||
path?: string; // setValue时,目标变量的path
|
||||
value?: string | {[key: string]: string}; // setValue时,目标变量的值
|
||||
index?: number; // setValue时,支持更新指定索引的数据,一般用于数组类型
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,34 +34,16 @@ export class CmptAction implements RendererAction {
|
||||
* 触发组件未指定id或未指定响应组件componentId,则使用触发组件响应
|
||||
*/
|
||||
const key = action.componentId || action.componentName;
|
||||
const dataMergeMode = action.dataMergeMode || 'merge';
|
||||
|
||||
let component = key
|
||||
? event.context.scoped?.[
|
||||
action.componentId ? 'getComponentById' : 'getComponentByName'
|
||||
](key)
|
||||
: renderer;
|
||||
: null;
|
||||
|
||||
const dataMergeMode = action.dataMergeMode || 'merge';
|
||||
|
||||
// 显隐&状态控制
|
||||
if (['show', 'hidden', 'visibility'].includes(action.actionType)) {
|
||||
let visibility =
|
||||
action.actionType === 'visibility'
|
||||
? action.args?.value
|
||||
: action.actionType === 'show';
|
||||
return renderer.props.statusStore.setVisible(key!, visibility as any);
|
||||
} else if (['static', 'nonstatic'].includes(action.actionType)) {
|
||||
return renderer.props.statusStore.setStatic(
|
||||
key!,
|
||||
action.actionType === 'static'
|
||||
);
|
||||
} else if (
|
||||
['enabled', 'disabled', 'usability'].includes(action.actionType)
|
||||
) {
|
||||
let usability =
|
||||
action.actionType === 'usability'
|
||||
? !action.args?.value
|
||||
: action.actionType === 'disabled';
|
||||
return renderer.props.statusStore.setDisable(key!, usability);
|
||||
if (key && !component) {
|
||||
throw Error('目标组件没有找到,请检查componentId或componentName是否正确');
|
||||
}
|
||||
|
||||
if (action.actionType === 'setValue') {
|
||||
|
@ -1,13 +1,12 @@
|
||||
import {Schema, SchemaNode} from '../types';
|
||||
import {RendererEvent} from '../utils/renderer-event';
|
||||
import {filter} from '../utils/tpl';
|
||||
import {
|
||||
RendererAction,
|
||||
ListenerAction,
|
||||
ListenerContext,
|
||||
registerAction
|
||||
} from './Action';
|
||||
import {createObject, filter, render} from '../index';
|
||||
import reject from 'lodash/reject';
|
||||
|
||||
export interface IAlertAction extends ListenerAction {
|
||||
actionType: 'alert';
|
||||
@ -76,7 +75,7 @@ export class DialogAction implements RendererAction {
|
||||
event,
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: action.dialog ?? action.args?.dialog,
|
||||
dialog: action.dialog,
|
||||
reload: 'none'
|
||||
},
|
||||
action.data
|
||||
|
@ -33,7 +33,7 @@ export class DrawerAction implements RendererAction {
|
||||
event,
|
||||
{
|
||||
actionType: 'drawer',
|
||||
drawer: action.drawer ?? action.args?.drawer,
|
||||
drawer: action.drawer,
|
||||
reload: 'none'
|
||||
},
|
||||
action.data
|
||||
|
64
packages/amis-core/src/actions/StatusAction.ts
Normal file
64
packages/amis-core/src/actions/StatusAction.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import {RendererEvent} from '../utils/renderer-event';
|
||||
import {
|
||||
RendererAction,
|
||||
ListenerAction,
|
||||
ListenerContext,
|
||||
registerAction
|
||||
} from './Action';
|
||||
|
||||
export interface IStatusAction extends ListenerAction {
|
||||
actionType:
|
||||
| 'static'
|
||||
| 'nonstatic'
|
||||
| 'show'
|
||||
| 'visibility'
|
||||
| 'hidden'
|
||||
| 'enabled'
|
||||
| 'disabled'
|
||||
| 'usability';
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态更新动作
|
||||
*
|
||||
* @export
|
||||
* @class StatusAction
|
||||
* @implements {Action}
|
||||
*/
|
||||
export class StatusAction implements RendererAction {
|
||||
async run(
|
||||
action: IStatusAction,
|
||||
renderer: ListenerContext,
|
||||
event: RendererEvent<any>
|
||||
) {
|
||||
/**
|
||||
* 根据唯一ID查找指定组件
|
||||
* 触发组件未指定id或未指定响应组件componentId,则使用触发组件响应
|
||||
*/
|
||||
const key = action.componentId || action.componentName;
|
||||
|
||||
// 显隐&状态控制
|
||||
if (['show', 'hidden', 'visibility'].includes(action.actionType)) {
|
||||
let visibility =
|
||||
action.actionType === 'visibility'
|
||||
? action.args?.value
|
||||
: action.actionType === 'show';
|
||||
return renderer.props.statusStore.setVisible(key!, visibility as any);
|
||||
} else if (['static', 'nonstatic'].includes(action.actionType)) {
|
||||
return renderer.props.statusStore.setStatic(
|
||||
key!,
|
||||
action.actionType === 'static'
|
||||
);
|
||||
} else if (
|
||||
['enabled', 'disabled', 'usability'].includes(action.actionType)
|
||||
) {
|
||||
let usability =
|
||||
action.actionType === 'usability'
|
||||
? !action.args?.value
|
||||
: action.actionType === 'disabled';
|
||||
return renderer.props.statusStore.setDisable(key!, usability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerAction('status', new StatusAction());
|
@ -10,6 +10,7 @@ import './ParallelAction';
|
||||
import './CustomAction';
|
||||
import './BroadcastAction';
|
||||
import './CmptAction';
|
||||
import './StatusAction';
|
||||
import './AjaxAction';
|
||||
import './CopyAction';
|
||||
import './DialogAction';
|
||||
|
@ -98,7 +98,13 @@ import Overlay from './components/Overlay';
|
||||
import PopOver from './components/PopOver';
|
||||
import {FormRenderer} from './renderers/Form';
|
||||
import type {FormHorizontal, FormSchemaBase} from './renderers/Form';
|
||||
import {enableDebug, promisify, replaceText, wrapFetcher} from './utils/index';
|
||||
import {
|
||||
enableDebug,
|
||||
disableDebug,
|
||||
promisify,
|
||||
replaceText,
|
||||
wrapFetcher
|
||||
} from './utils/index';
|
||||
import type {OnEventProps} from './utils/index';
|
||||
import {valueMap as styleMap} from './utils/style-helper';
|
||||
import {RENDERER_TRANSMISSION_OMIT_PROPS} from './SchemaRenderer';
|
||||
@ -195,7 +201,9 @@ export {
|
||||
OnEventProps,
|
||||
FormSchemaBase,
|
||||
filterTarget,
|
||||
CustomStyle
|
||||
CustomStyle,
|
||||
enableDebug,
|
||||
disableDebug
|
||||
};
|
||||
|
||||
export function render(
|
||||
@ -257,13 +265,6 @@ function AMISRenderer({
|
||||
translate
|
||||
} as any;
|
||||
|
||||
if (options.enableAMISDebug) {
|
||||
// 因为里面还有 render
|
||||
setTimeout(() => {
|
||||
enableDebug();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
store = RendererStore.create({}, options);
|
||||
stores[options.session || 'global'] = store;
|
||||
} else {
|
||||
@ -291,6 +292,11 @@ function AMISRenderer({
|
||||
}
|
||||
env.theme = getTheme(theme);
|
||||
|
||||
React.useEffect(() => {
|
||||
env.enableAMISDebug ? enableDebug() : disableDebug();
|
||||
return () => env.enableAMISDebug || disableDebug();
|
||||
}, [env.enableAMISDebug]);
|
||||
|
||||
if (props.locale !== undefined) {
|
||||
env.translate = translate;
|
||||
env.locale = locale;
|
||||
|
@ -1000,26 +1000,25 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
|
||||
handleBulkChange(values: Object, submit: boolean) {
|
||||
const {onChange, store, formLazyChange} = this.props;
|
||||
store.setValues(values);
|
||||
// store.updateData(values);
|
||||
|
||||
store.updateData(values);
|
||||
|
||||
store.items.forEach(formItem => {
|
||||
const updatedValue = getVariable(values, formItem.name, false);
|
||||
|
||||
if (updatedValue !== undefined) {
|
||||
// 更新验证状态但保留错误信息
|
||||
formItem.reset(true);
|
||||
// 这里需要更新value,否则提交时不会使用新的字段值校验
|
||||
formItem.changeTmpValue(updatedValue);
|
||||
formItem.validateOnChange && formItem.validate(values);
|
||||
}
|
||||
});
|
||||
// store.items.forEach(formItem => {
|
||||
// const updatedValue = getVariable(values, formItem.name, false);
|
||||
|
||||
// if (updatedValue !== undefined) {
|
||||
// // 更新验证状态但保留错误信息
|
||||
// formItem.reset(true);
|
||||
// // 这里需要更新value,否则提交时不会使用新的字段值校验
|
||||
// formItem.changeTmpValue(updatedValue);
|
||||
// formItem.validateOnChange && formItem.validate(values);
|
||||
// }
|
||||
// });
|
||||
(formLazyChange === false ? this.emitChange : this.lazyEmitChange)(submit);
|
||||
}
|
||||
|
||||
handleFormSubmit(e: React.UIEvent<any>) {
|
||||
const {preventEnterSubmit, onActionSensor} = this.props;
|
||||
const {preventEnterSubmit, onActionSensor, close} = this.props;
|
||||
|
||||
e.preventDefault();
|
||||
if (preventEnterSubmit) {
|
||||
@ -1029,7 +1028,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
const sensor: any = this.handleAction(
|
||||
e,
|
||||
{
|
||||
type: 'submit'
|
||||
type: 'submit',
|
||||
close
|
||||
},
|
||||
this.props.store.data
|
||||
);
|
||||
@ -1638,7 +1638,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
dispatchEvent,
|
||||
labelAlign,
|
||||
labelWidth,
|
||||
static: isStatic
|
||||
static: isStatic,
|
||||
canAccessSuperData
|
||||
} = props;
|
||||
|
||||
const subProps = {
|
||||
|
@ -72,6 +72,11 @@ export interface FormBaseControl extends BaseSchemaWithoutType {
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* 额外的字段名,当为范围组件时可以用来将另外一个值打平出来
|
||||
*/
|
||||
extraName?: string;
|
||||
|
||||
/**
|
||||
* 显示一个小图标, 鼠标放上去的时候显示提示内容
|
||||
*/
|
||||
|
@ -18,8 +18,7 @@ import {
|
||||
isNeedFormula,
|
||||
isExpression,
|
||||
FormulaExec,
|
||||
replaceExpression,
|
||||
isNowFormula
|
||||
replaceExpression
|
||||
} from '../utils/formula';
|
||||
import {IIRendererStore, IRendererStore} from '../store';
|
||||
import {ScopedContext, IScopedContext} from '../Scoped';
|
||||
@ -130,6 +129,7 @@ export function wrapControl<
|
||||
validationErrors,
|
||||
unique,
|
||||
value,
|
||||
extraName,
|
||||
multiple,
|
||||
delimiter,
|
||||
valueField,
|
||||
@ -209,7 +209,8 @@ export function wrapControl<
|
||||
maxLength,
|
||||
validateOnChange,
|
||||
label,
|
||||
inputGroupControl
|
||||
inputGroupControl,
|
||||
extraName
|
||||
});
|
||||
|
||||
// issue 这个逻辑应该在 combo 里面自己实现。
|
||||
@ -225,16 +226,41 @@ export function wrapControl<
|
||||
// 同步 value: 优先使用 props 中的 value
|
||||
model.changeTmpValue(propValue, 'controlled');
|
||||
} else {
|
||||
// 备注: 此处的 value 是 schema 中的 value(和props.defaultValue相同)
|
||||
const isExp = isExpression(value);
|
||||
const curTmpValue = isExp
|
||||
? FormulaExec['formula'](value, data) // 对组件默认值进行运算
|
||||
: store?.getValueByName(model.name) ?? replaceExpression(value); // 优先使用公式表达式
|
||||
// 同步 value
|
||||
model.changeTmpValue(
|
||||
curTmpValue,
|
||||
isExp ? 'formulaChanged' : 'defaultValue'
|
||||
);
|
||||
|
||||
if (isExp) {
|
||||
model.changeTmpValue(
|
||||
FormulaExec['formula'](value, data), // 对组件默认值进行运算
|
||||
'formulaChanged'
|
||||
);
|
||||
} else {
|
||||
let initialValue = model.extraName
|
||||
? [
|
||||
store?.getValueByName(
|
||||
model.name,
|
||||
form?.canAccessSuperData
|
||||
),
|
||||
store?.getValueByName(
|
||||
model.extraName,
|
||||
form?.canAccessSuperData
|
||||
)
|
||||
]
|
||||
: store?.getValueByName(model.name, form?.canAccessSuperData);
|
||||
|
||||
if (
|
||||
model.extraName &&
|
||||
initialValue.every((item: any) => item === undefined)
|
||||
) {
|
||||
initialValue = undefined;
|
||||
}
|
||||
|
||||
model.changeTmpValue(
|
||||
initialValue ?? replaceExpression(value),
|
||||
typeof initialValue !== 'undefined'
|
||||
? 'initialValue'
|
||||
: 'defaultValue'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
@ -243,7 +269,13 @@ export function wrapControl<
|
||||
model.tmpValue !== undefined
|
||||
) {
|
||||
// 组件默认值支持表达式需要: 避免初始化时上下文中丢失组件默认值
|
||||
onChange(model.tmpValue, model.name, false, true);
|
||||
if (model.extraName) {
|
||||
const values = model.splitExtraValue(model.tmpValue);
|
||||
onChange(values[0], model.name, false, true);
|
||||
onChange(values[1], model.extraName, false, true);
|
||||
} else {
|
||||
onChange(model.tmpValue, model.name, false, true);
|
||||
}
|
||||
} else if (
|
||||
onChange &&
|
||||
typeof propValue === 'undefined' &&
|
||||
@ -254,7 +286,13 @@ export function wrapControl<
|
||||
store?.storeType !== TableStore.name
|
||||
) {
|
||||
// 如果没有初始值,通过 onChange 设置过去
|
||||
onChange(model.tmpValue, model.name, false, true);
|
||||
if (model.extraName) {
|
||||
const values = model.splitExtraValue(model.tmpValue);
|
||||
onChange(values[0], model.name, false, true);
|
||||
onChange(values[1], model.extraName, false, true);
|
||||
} else {
|
||||
onChange(model.tmpValue, model.name, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,7 +357,8 @@ export function wrapControl<
|
||||
'validateApi',
|
||||
'minLength',
|
||||
'maxLength',
|
||||
'label'
|
||||
'label',
|
||||
'extraName'
|
||||
],
|
||||
prevProps.$schema,
|
||||
props.$schema
|
||||
@ -346,7 +385,8 @@ export function wrapControl<
|
||||
minLength: props.$schema.minLength,
|
||||
maxLength: props.$schema.maxLength,
|
||||
label: props.$schema.label,
|
||||
inputGroupControl: props?.inputGroupControl
|
||||
inputGroupControl: props?.inputGroupControl,
|
||||
extraName: props.$schema.extraName
|
||||
});
|
||||
}
|
||||
|
||||
@ -363,45 +403,39 @@ export function wrapControl<
|
||||
} else if (
|
||||
model &&
|
||||
typeof props.defaultValue !== 'undefined' &&
|
||||
isExpression(props.defaultValue)
|
||||
) {
|
||||
let nowFormulaChecked = false;
|
||||
// 渲染器中的 defaultValue 优先(备注: SchemaRenderer中会将 value 改成 defaultValue)
|
||||
if (
|
||||
!isEqual(props.defaultValue, prevProps.defaultValue) ||
|
||||
isExpression(props.defaultValue) &&
|
||||
(!isEqual(props.defaultValue, prevProps.defaultValue) ||
|
||||
(props.data !== prevProps.data &&
|
||||
(isNeedFormula(
|
||||
isNeedFormula(
|
||||
props.defaultValue,
|
||||
props.data,
|
||||
prevProps.data
|
||||
) ||
|
||||
(nowFormulaChecked = isNowFormula(props.defaultValue))))
|
||||
)))
|
||||
) {
|
||||
const curResult = FormulaExec['formula'](
|
||||
props.defaultValue,
|
||||
props.data
|
||||
);
|
||||
const prevResult = FormulaExec['formula'](
|
||||
prevProps.defaultValue,
|
||||
prevProps.data
|
||||
);
|
||||
if (
|
||||
!isEqual(curResult, prevResult) &&
|
||||
!isEqual(curResult, model.tmpValue)
|
||||
) {
|
||||
const curResult = FormulaExec['formula'](
|
||||
props.defaultValue,
|
||||
props.data
|
||||
);
|
||||
const prevResult = FormulaExec['formula'](
|
||||
prevProps.defaultValue,
|
||||
prevProps.data
|
||||
);
|
||||
if (
|
||||
!isEqual(curResult, prevResult) &&
|
||||
!isEqual(curResult, model.tmpValue)
|
||||
) {
|
||||
// 识别上下文变动、自身数值变动、公式运算结果变动
|
||||
model.changeTmpValue(curResult, 'formulaChanged');
|
||||
// 识别上下文变动、自身数值变动、公式运算结果变动
|
||||
model.changeTmpValue(curResult, 'formulaChanged');
|
||||
|
||||
if (model.extraName) {
|
||||
const values = model.splitExtraValue(curResult);
|
||||
props.onChange?.(values[0], model.name, false);
|
||||
props.onChange?.(values[1], model.extraName, false);
|
||||
} else {
|
||||
props.onChange?.(curResult, model.name, false);
|
||||
} else if (nowFormulaChecked) {
|
||||
const nowData = props.data[model.name];
|
||||
// now 表达式,计算后的值永远相同
|
||||
model.changeTmpValue(nowData, 'formulaChanged');
|
||||
props.onChange?.(nowData, model.name, false);
|
||||
}
|
||||
}
|
||||
} else if (model) {
|
||||
const valueByName = getVariable(props.data, model.name);
|
||||
|
||||
// value 非公式表达式时,name 值优先,若 defaultValue 主动变动时,则使用 defaultValue
|
||||
if (
|
||||
// 然后才是查看关联的 name 属性值是否变化
|
||||
@ -410,12 +444,30 @@ export function wrapControl<
|
||||
isEqual(model.emitedValue, model.tmpValue))
|
||||
) {
|
||||
model.changeEmitedValue(undefined);
|
||||
const prevValueByName = getVariable(props.data, model.name);
|
||||
const valueByName = model.extraName
|
||||
? [
|
||||
getVariable(props.data, model.name, false),
|
||||
getVariable(props.data, model.extraName, false)
|
||||
]
|
||||
: getVariable(props.data, model.name, false);
|
||||
|
||||
if (
|
||||
(!isEqual(valueByName, prevValueByName) ||
|
||||
getVariable(props.data, model.name, false) !==
|
||||
getVariable(prevProps.data, model.name, false)) &&
|
||||
!isEqual(valueByName, model.tmpValue)
|
||||
!isEqual(
|
||||
valueByName,
|
||||
model.extraName
|
||||
? model.splitExtraValue(model.tmpValue)
|
||||
: model.tmpValue
|
||||
) &&
|
||||
(!isEqual(
|
||||
model.extraName ? valueByName[0] : valueByName,
|
||||
getVariable(prevProps.data, model.name, false)
|
||||
) ||
|
||||
// extraName
|
||||
(model.extraName &&
|
||||
!isEqual(
|
||||
valueByName[1],
|
||||
getVariable(prevProps.data, model.extraName, false)
|
||||
)))
|
||||
) {
|
||||
model.changeTmpValue(
|
||||
valueByName,
|
||||
@ -633,10 +685,18 @@ export function wrapControl<
|
||||
if (!this.model) {
|
||||
return;
|
||||
}
|
||||
const model = this.model;
|
||||
const value = this.model.tmpValue;
|
||||
const oldValue = getVariable(data, this.model.name, false);
|
||||
const oldValue = model.extraName
|
||||
? [
|
||||
getVariable(data, model.name, false),
|
||||
getVariable(data, model.extraName, false)
|
||||
]
|
||||
: getVariable(data, model.name, false);
|
||||
|
||||
if (oldValue === value) {
|
||||
if (
|
||||
model.extraName ? isEqual(oldValue, value) : oldValue === value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -668,7 +728,13 @@ export function wrapControl<
|
||||
return;
|
||||
}
|
||||
|
||||
onChange?.(value, name!, submitOnChange === true);
|
||||
if (model.extraName) {
|
||||
const values = model.splitExtraValue(value);
|
||||
onChange?.(values[0], name!);
|
||||
onChange?.(values[1], model.extraName, submitOnChange === true);
|
||||
} else {
|
||||
onChange?.(value, name!, submitOnChange === true);
|
||||
}
|
||||
this.checkValidate();
|
||||
}
|
||||
|
||||
@ -690,6 +756,7 @@ export function wrapControl<
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.model;
|
||||
const {
|
||||
formStore: form,
|
||||
name,
|
||||
@ -703,7 +770,13 @@ export function wrapControl<
|
||||
value = pipeOut(value, oldValue, data);
|
||||
}
|
||||
|
||||
onChange?.(value, name!, false, true);
|
||||
if (model.extraName) {
|
||||
const values = model.splitExtraValue(value);
|
||||
onChange?.(values[0], name!, false, true);
|
||||
onChange?.(values[1], model.extraName!, false, true);
|
||||
} else {
|
||||
onChange?.(value, name!, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
@ -51,7 +51,9 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
|
||||
if (current.storeType === 'FormItemStore') {
|
||||
formItems.push(current);
|
||||
} else {
|
||||
} else if (
|
||||
!['ComboStore', 'TableStore', 'FormStore'].includes(current.storeType)
|
||||
) {
|
||||
pool.push(...current.children);
|
||||
}
|
||||
}
|
||||
@ -68,33 +70,10 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
return getItems();
|
||||
},
|
||||
|
||||
/**
|
||||
* 相对于 items(), 只收集直接子formItem
|
||||
* 避免 子form 表单项的重复验证
|
||||
*/
|
||||
get directItems() {
|
||||
const formItems: Array<IFormItemStore> = [];
|
||||
|
||||
// 查找孩子节点中是 formItem 的表单项
|
||||
const pool = self.children.concat();
|
||||
while (pool.length) {
|
||||
const current = pool.shift()!;
|
||||
if (current.storeType === 'FormItemStore') {
|
||||
formItems.push(current);
|
||||
} else if (
|
||||
!['ComboStore', 'TableStore'].includes(current.storeType)
|
||||
) {
|
||||
pool.push(...current.children);
|
||||
}
|
||||
}
|
||||
|
||||
return formItems;
|
||||
},
|
||||
|
||||
/** 获取InputGroup的子元素 */
|
||||
get inputGroupItems() {
|
||||
const formItems: Record<string, IFormItemStore[]> = {};
|
||||
const children: Array<any> = this.directItems.concat();
|
||||
const children: Array<any> = this.items.concat();
|
||||
|
||||
while (children.length) {
|
||||
const current = children.shift();
|
||||
@ -190,11 +169,31 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
|
||||
// 如果数据域中有数据变化,就都reset一下,去掉之前残留的验证消息
|
||||
self.items.forEach(item => {
|
||||
const value = getVariable(values, item.name);
|
||||
if (value !== undefined && value !== item.tmpValue) {
|
||||
item.changeTmpValue(value);
|
||||
if (item.extraName) {
|
||||
const value = [
|
||||
getVariable(values, item.name, false),
|
||||
getVariable(values, item.extraName, false)
|
||||
];
|
||||
if (
|
||||
value.some(item => item !== undefined) &&
|
||||
!isEqual(value, item.tmpValue)
|
||||
) {
|
||||
const origin = item.splitExtraValue(item.tmpValue);
|
||||
item.changeTmpValue(
|
||||
value.map((item, idx) => item ?? origin[idx]),
|
||||
'dataChanged'
|
||||
);
|
||||
item.changeEmitedValue(undefined);
|
||||
}
|
||||
} else {
|
||||
const value = getVariable(values, item.name, false);
|
||||
if (value !== undefined && value !== item.tmpValue) {
|
||||
item.changeTmpValue(value, 'dataChanged');
|
||||
item.changeEmitedValue(undefined);
|
||||
}
|
||||
}
|
||||
item.reset();
|
||||
item.validateOnChange && item.validate(self.data);
|
||||
});
|
||||
|
||||
// 同步 options
|
||||
@ -578,7 +577,7 @@ export const FormStore = ServiceStore.named('FormStore')
|
||||
validateErrCb?: () => void
|
||||
) {
|
||||
self.validated = true;
|
||||
const items = self.directItems.concat();
|
||||
const items = self.items.concat();
|
||||
for (let i = 0, len = items.length; i < len; i++) {
|
||||
let item = items[i] as IFormItemStore;
|
||||
|
||||
|
@ -66,6 +66,7 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
messages: types.optional(types.frozen(), {}),
|
||||
errorData: types.optional(types.array(ErrorDetail), []),
|
||||
name: types.string,
|
||||
extraName: '',
|
||||
itemId: '', // 因为 name 可能会重名,所以加个 id 进来,如果有需要用来定位具体某一个
|
||||
unsetValueOnInvisible: false,
|
||||
itemsRef: types.optional(types.array(types.string), []),
|
||||
@ -210,6 +211,15 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
});
|
||||
|
||||
return selectedOptions;
|
||||
},
|
||||
splitExtraValue(value: any) {
|
||||
const delimiter = self.delimiter || ',';
|
||||
const values = Array.isArray(value)
|
||||
? value
|
||||
: typeof value === 'string'
|
||||
? value.split(delimiter || ',')
|
||||
: [];
|
||||
return values;
|
||||
}
|
||||
};
|
||||
})
|
||||
@ -220,6 +230,7 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
let loadAutoUpdateCancel: Function | null = null;
|
||||
|
||||
function config({
|
||||
extraName,
|
||||
required,
|
||||
unique,
|
||||
value,
|
||||
@ -244,6 +255,7 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
label,
|
||||
inputGroupControl
|
||||
}: {
|
||||
extraName?: string;
|
||||
required?: boolean;
|
||||
unique?: boolean;
|
||||
value?: any;
|
||||
@ -276,6 +288,7 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
rules = str2rules(rules);
|
||||
}
|
||||
|
||||
typeof extraName !== 'undefined' && (self.extraName = extraName);
|
||||
typeof type !== 'undefined' && (self.type = type);
|
||||
typeof id !== 'undefined' && (self.itemId = id);
|
||||
typeof messages !== 'undefined' && (self.messages = messages);
|
||||
@ -1236,6 +1249,7 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
function changeTmpValue(
|
||||
value: any,
|
||||
changeReason?:
|
||||
| 'initialValue' // 初始值,读取与当前数据域,或者上层数据域
|
||||
| 'formInited' // 表单初始化
|
||||
| 'dataChanged' // 表单数据变化
|
||||
| 'formulaChanged' // 公式运算结果变化
|
||||
|
@ -217,13 +217,17 @@ export interface ApiObject extends BaseApiObject {
|
||||
operationName?: string;
|
||||
body?: PlainObject;
|
||||
query?: PlainObject;
|
||||
mockResponse?: PlainObject;
|
||||
adaptor?: (
|
||||
payload: object,
|
||||
response: fetcherResult,
|
||||
api: ApiObject,
|
||||
context: any
|
||||
) => any;
|
||||
requestAdaptor?: (api: ApiObject, context: any) => ApiObject;
|
||||
requestAdaptor?: (
|
||||
api: ApiObject,
|
||||
context: any
|
||||
) => ApiObject | Promise<ApiObject>;
|
||||
/** 是否过滤为空字符串的 query 参数 */
|
||||
filterEmptyQuery?: boolean;
|
||||
}
|
||||
@ -641,6 +645,21 @@ export interface BaseSchemaWithoutType {
|
||||
style?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* 编辑器配置,运行时可以忽略
|
||||
*/
|
||||
editorSetting?: {
|
||||
/**
|
||||
* 组件名称,通常是业务名称方便定位
|
||||
*/
|
||||
displayName?: string;
|
||||
|
||||
/**
|
||||
* 编辑器假数据,方便展示
|
||||
*/
|
||||
mock?: any;
|
||||
};
|
||||
}
|
||||
|
||||
export type OperatorType =
|
||||
|
@ -76,7 +76,7 @@ export function buildApi(
|
||||
}
|
||||
|
||||
if (api.requestAdaptor && typeof api.requestAdaptor === 'string') {
|
||||
api.requestAdaptor = str2function(
|
||||
api.requestAdaptor = str2AsyncFunction(
|
||||
api.requestAdaptor,
|
||||
'api',
|
||||
'context'
|
||||
@ -84,7 +84,7 @@ export function buildApi(
|
||||
}
|
||||
|
||||
if (api.adaptor && typeof api.adaptor === 'string') {
|
||||
api.adaptor = str2function(
|
||||
api.adaptor = str2AsyncFunction(
|
||||
api.adaptor,
|
||||
'payload',
|
||||
'response',
|
||||
@ -464,12 +464,16 @@ export function wrapFetcher(
|
||||
return fn as any;
|
||||
}
|
||||
|
||||
const wrappedFetcher = function (api: Api, data: object, options?: object) {
|
||||
const wrappedFetcher = async function (
|
||||
api: Api,
|
||||
data: object,
|
||||
options?: object
|
||||
) {
|
||||
api = buildApi(api, data, options) as ApiObject;
|
||||
|
||||
if (api.requestAdaptor) {
|
||||
debug('api', 'before requestAdaptor', api);
|
||||
api = api.requestAdaptor(api, data) || api;
|
||||
api = (await api.requestAdaptor(api, data)) || api;
|
||||
debug('api', 'after requestAdaptor', api);
|
||||
}
|
||||
|
||||
@ -501,6 +505,12 @@ export function wrapFetcher(
|
||||
api.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
// 如果发送适配器中设置了 mockResponse
|
||||
// 则直接跳过请求发送
|
||||
if (api.mockResponse) {
|
||||
return wrapAdaptor(Promise.resolve(api.mockResponse) as any, api, data);
|
||||
}
|
||||
|
||||
if (!isValidApi(api.url)) {
|
||||
throw new Error(`invalid api url:${api.url}`);
|
||||
}
|
||||
|
@ -2,9 +2,10 @@
|
||||
* amis 运行时调试功能,为了避免循环引用,这个组件不要依赖 amis 里的组件
|
||||
*/
|
||||
|
||||
import React, {Component, useEffect, useRef, useState} from 'react';
|
||||
import React, {Component, useEffect, useRef, useState, version} from 'react';
|
||||
import cx from 'classnames';
|
||||
import {findDOMNode, render} from 'react-dom';
|
||||
import {findDOMNode, render, unmountComponentAtNode} from 'react-dom';
|
||||
// import {createRoot} from 'react-dom/client';
|
||||
import {autorun, observable} from 'mobx';
|
||||
import {observer} from 'mobx-react';
|
||||
import {uuidv4} from './helper';
|
||||
@ -84,16 +85,18 @@ const LogView = observer(({store}: {store: AMISDebugStore}) => {
|
||||
[{log.cat}] {log.msg}
|
||||
</div>
|
||||
{log.ext ? (
|
||||
<JsonView
|
||||
name={null}
|
||||
theme="monokai"
|
||||
src={JSON.parse(log.ext)}
|
||||
collapsed={true}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapseStringsAfterLength={ellipsisThreshold}
|
||||
iconStyle="square"
|
||||
/>
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<JsonView
|
||||
name={null}
|
||||
theme="monokai"
|
||||
src={JSON.parse(log.ext)}
|
||||
collapsed={true}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapseStringsAfterLength={ellipsisThreshold}
|
||||
iconStyle="square"
|
||||
/>
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
@ -126,16 +129,18 @@ const AMISDebug = observer(({store}: {store: AMISDebugStore}) => {
|
||||
stackDataView.push(
|
||||
<div key={`data-${level}`}>
|
||||
<h3>Data Level-{level}</h3>
|
||||
<JsonView
|
||||
key={`dataview-${stack}`}
|
||||
name={null}
|
||||
theme="monokai"
|
||||
src={stack}
|
||||
collapsed={level === 0 ? false : true}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
iconStyle="square"
|
||||
/>
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<JsonView
|
||||
key={`dataview-${stack}`}
|
||||
name={null}
|
||||
theme="monokai"
|
||||
src={stack}
|
||||
collapsed={level === 0 ? false : true}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
iconStyle="square"
|
||||
/>
|
||||
</React.Suspense>
|
||||
</div>
|
||||
);
|
||||
level += 1;
|
||||
@ -319,7 +324,7 @@ function handleMouseclick(e: MouseEvent) {
|
||||
}
|
||||
const dom = e.target as HTMLElement;
|
||||
const target = dom.closest(`[data-debug-id]`);
|
||||
if (target) {
|
||||
if (target && !target.closest('.AMISDebug')) {
|
||||
store.activeId = target.getAttribute('data-debug-id')!;
|
||||
store.tab = 'inspect';
|
||||
}
|
||||
@ -366,6 +371,7 @@ autorun(() => {
|
||||
|
||||
// 页面中只能有一个实例
|
||||
let isEnabled = false;
|
||||
let unmount: () => void;
|
||||
|
||||
export function enableDebug() {
|
||||
if (isEnabled) {
|
||||
@ -376,7 +382,21 @@ export function enableDebug() {
|
||||
const amisDebugElement = document.createElement('div');
|
||||
document.body.appendChild(amisDebugElement);
|
||||
const element = <AMISDebug store={store} />;
|
||||
|
||||
// if (parseInt(version.split('.')[0], 10) >= 18) {
|
||||
// const root = createRoot(amisDebugElement);
|
||||
// root.render(element);
|
||||
// unmount = () => {
|
||||
// root.unmount();
|
||||
// document.body.removeChild(amisDebugElement);
|
||||
// };
|
||||
// } else {
|
||||
render(element, amisDebugElement);
|
||||
unmount = () => {
|
||||
unmountComponentAtNode(amisDebugElement);
|
||||
document.body.removeChild(amisDebugElement);
|
||||
};
|
||||
// }
|
||||
|
||||
document.body.appendChild(amisHoverBox);
|
||||
document.body.appendChild(amisActiveBox);
|
||||
@ -384,6 +404,18 @@ export function enableDebug() {
|
||||
document.addEventListener('click', handleMouseclick);
|
||||
}
|
||||
|
||||
export function disableDebug() {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
isEnabled = false;
|
||||
unmount?.();
|
||||
document.body.removeChild(amisHoverBox);
|
||||
document.body.removeChild(amisActiveBox);
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('click', handleMouseclick);
|
||||
}
|
||||
|
||||
interface DebugWrapperProps {
|
||||
renderer: any;
|
||||
children?: React.ReactNode;
|
||||
|
@ -225,7 +225,8 @@ export function isNeedFormula(
|
||||
const variables = FormulaExec.collect(expression);
|
||||
return variables.some(
|
||||
(variable: string) =>
|
||||
FormulaExec.var(variable, prevData) !== FormulaExec.var(variable, curData)
|
||||
FormulaExec.var(variable, prevData) !==
|
||||
FormulaExec.var(variable, curData)
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
@ -238,11 +239,6 @@ export function isNeedFormula(
|
||||
}
|
||||
}
|
||||
|
||||
export function isNowFormula(expression: string): boolean {
|
||||
const block = expression.split(/\${|\||}/).filter(item => item);
|
||||
return block[1] === 'now';
|
||||
}
|
||||
|
||||
// 将 \${xx} 替换成 ${xx}
|
||||
export function replaceExpression(expression: any): any {
|
||||
if (
|
||||
|
@ -31,7 +31,7 @@
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
margin-right: #{px2rem(10px)};
|
||||
max-width: calc(100% - 52px);
|
||||
// max-width: calc(100% - 52px);
|
||||
height: var(--Button--sm-height);
|
||||
|
||||
& > input {
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
import cx from 'classnames';
|
||||
import {prompt, toast} from 'amis';
|
||||
import debounce from 'lodash/debounce';
|
||||
import isArray from 'lodash/isArray';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import {parse, stringify} from 'json-ast-comments';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
@ -115,12 +116,17 @@ export default class AMisCodeEditor extends React.Component<AMisCodeEditorProps>
|
||||
obj2str(value: any, props: AMisCodeEditorProps) {
|
||||
// 隐藏公共配置
|
||||
value = filterSchemaForConfig(value);
|
||||
value = {
|
||||
type: value?.type,
|
||||
...value
|
||||
};
|
||||
|
||||
if (!value.type && props.$schema?.match(/PageSchema/i)) {
|
||||
if (!isArray(value)) {
|
||||
value = {
|
||||
type: value?.type,
|
||||
...value
|
||||
};
|
||||
}
|
||||
|
||||
if (isArray(value)) {
|
||||
return stringify(value);
|
||||
} else if (!value.type && props.$schema?.match(/PageSchema/i)) {
|
||||
value.type = 'page';
|
||||
} else if (!value.type) {
|
||||
delete value.type;
|
||||
@ -153,7 +159,7 @@ export default class AMisCodeEditor extends React.Component<AMisCodeEditorProps>
|
||||
const {onChange, value} = this.props;
|
||||
let ret: any = this.str2obj(this.state.contents);
|
||||
|
||||
if (!ret || !isPlainObject(ret)) {
|
||||
if (!ret || (!isPlainObject(ret) && !isArray(ret))) {
|
||||
this.setState({
|
||||
wrongSchema: this.state.contents
|
||||
});
|
||||
|
@ -107,7 +107,9 @@ export function makeWrapper(
|
||||
if (info.name) {
|
||||
const nodeSchema = manager.store.getNodeById(info.id)?.schema;
|
||||
const tag = nodeSchema?.title ?? nodeSchema?.name;
|
||||
manager.dataSchema.current.tag = `${tag ?? ''}「${info.name}」`;
|
||||
manager.dataSchema.current.tag = `${info.name}${
|
||||
tag ? ` : ${tag}` : ''
|
||||
}`;
|
||||
manager.dataSchema.current.group = '组件上下文';
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,8 @@ export function autoPreRegisterEditorCustomPlugins() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册Editor插件。
|
||||
* 注册Editor插件
|
||||
* 备注: 支持覆盖原有组件,注册新的组件时需配置 priority。
|
||||
* @param editor
|
||||
*/
|
||||
export function registerEditorPlugin(klass: PluginClass) {
|
||||
@ -1939,6 +1940,13 @@ export class EditorManager {
|
||||
let region = node;
|
||||
const trigger = node;
|
||||
|
||||
// 删掉当前行记录scope,保持原始scope
|
||||
for (const key in this.dataSchema.idMap) {
|
||||
if (/\-currentRow$/.test(key)) {
|
||||
this.dataSchema.removeScope(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 查找最近一层的数据域
|
||||
while (!scope && from) {
|
||||
const nodeId = from.info?.id;
|
||||
@ -1963,12 +1971,13 @@ export class EditorManager {
|
||||
if (!nearestScope && node && !node.isSecondFactor) {
|
||||
nearestScope = scope;
|
||||
}
|
||||
|
||||
const jsonschema = await node?.info?.plugin?.buildDataSchemas?.(
|
||||
node,
|
||||
region,
|
||||
trigger
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
|
||||
if (jsonschema) {
|
||||
scope.removeSchema(jsonschema.$id);
|
||||
scope.addSchema(jsonschema);
|
||||
@ -1977,7 +1986,16 @@ export class EditorManager {
|
||||
scope = withoutSuper ? undefined : scope.parent;
|
||||
}
|
||||
|
||||
if (nearestScope?.id) {
|
||||
// 存在当前行时,找到最底层(todo:暂不考虑table套service+table的场景)
|
||||
const nearestScopeId = Object.keys(this.dataSchema.idMap).find(
|
||||
key =>
|
||||
/\-currentRow$/.test(key) &&
|
||||
!this.dataSchema.idMap[key].children?.length
|
||||
);
|
||||
|
||||
if (nearestScopeId) {
|
||||
this.dataSchema.switchTo(nearestScopeId);
|
||||
} else if (nearestScope?.id) {
|
||||
this.dataSchema.switchTo(nearestScope.id);
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {EditorDNDManager} from './dnd';
|
||||
import React from 'react';
|
||||
import {DiffChange} from './util';
|
||||
import find from 'lodash/find';
|
||||
import type {RendererConfig} from 'amis-core';
|
||||
import type {RendererConfig, Schema} from 'amis-core';
|
||||
import type {MenuDivider, MenuItem} from 'amis-ui/lib/components/ContextMenu';
|
||||
import type {BaseSchema, SchemaCollection} from 'amis';
|
||||
import type {AsyncLayerOptions} from './component/AsyncLayer';
|
||||
@ -916,7 +916,8 @@ export interface PluginInterface
|
||||
buildDataSchemas?: (
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
trigger?: EditorNodeType,
|
||||
parent?: EditorNodeType
|
||||
) => any | Promise<any>;
|
||||
|
||||
rendererBeforeDispatchEvent?: (
|
||||
@ -925,6 +926,19 @@ export interface PluginInterface
|
||||
data: any
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* 给 schema 打补丁,纠正一下 schema 配置。
|
||||
* @param schema
|
||||
* @param renderer
|
||||
* @param props
|
||||
* @returns
|
||||
*/
|
||||
patchSchema?: (
|
||||
schema: Schema,
|
||||
renderer: RendererConfig,
|
||||
props?: any
|
||||
) => Schema | void;
|
||||
|
||||
dispose?: () => void;
|
||||
}
|
||||
|
||||
@ -1208,6 +1222,22 @@ export abstract class BasePlugin implements PluginInterface {
|
||||
: plugin instanceof rendererNameOrKlass
|
||||
);
|
||||
}
|
||||
|
||||
buildDataSchemas(
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType,
|
||||
parent?: EditorNodeType
|
||||
) {
|
||||
return {
|
||||
type: 'string',
|
||||
title:
|
||||
typeof node.schema.label === 'string'
|
||||
? node.schema.label
|
||||
: node.schema.name,
|
||||
originalValue: node.schema.value // 记录原始值,循环引用检测需要
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -649,9 +649,19 @@ export const EditorNode = types
|
||||
);
|
||||
}
|
||||
|
||||
// 调用 amis 纠错补丁
|
||||
patched = filterSchema(patched, {
|
||||
component: info.renderer.component
|
||||
} as any);
|
||||
// 调用插件上的补丁
|
||||
patched =
|
||||
info.plugin?.patchSchema?.(
|
||||
patched,
|
||||
{
|
||||
component: info.renderer.component
|
||||
},
|
||||
component?.props
|
||||
) || patched;
|
||||
patched = JSONPipeIn(patched);
|
||||
if (patched !== schema) {
|
||||
root.changeValueById(info.id, patched, undefined, true, true);
|
||||
|
@ -2023,7 +2023,8 @@ export class CRUDPlugin extends BasePlugin {
|
||||
async buildDataSchemas(
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
trigger?: EditorNodeType,
|
||||
parent?: EditorNodeType
|
||||
) {
|
||||
const child: EditorNodeType = node.children.find(
|
||||
item => !!~['table', 'table2', 'cards', 'list'].indexOf(item.type)
|
||||
@ -2036,12 +2037,12 @@ export class CRUDPlugin extends BasePlugin {
|
||||
let childSchame = await child.info.plugin.buildDataSchemas(
|
||||
child,
|
||||
undefined,
|
||||
trigger
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
|
||||
// 兼容table的rows,并自行merged异步数据
|
||||
if (child.type === 'table') {
|
||||
let cellProperties: any = {}; // 收集当前行记录中的列
|
||||
let itemsSchema: any = {}; // 收集选择记录中的列
|
||||
const columns: EditorNodeType = child.children.find(
|
||||
item => item.isRegion && item.region === 'columns'
|
||||
@ -2068,25 +2069,34 @@ export class CRUDPlugin extends BasePlugin {
|
||||
...rowsSchema?.properties
|
||||
};
|
||||
|
||||
Object.keys(tmpProperties).map(key => {
|
||||
if (isColumnChild) {
|
||||
// 给列补充group
|
||||
cellProperties[key] = {
|
||||
...cellProperties[key],
|
||||
group: '当前行记录'
|
||||
if (isColumnChild) {
|
||||
Object.keys(tmpProperties).map(key => {
|
||||
itemsSchema[key] = {
|
||||
...tmpProperties[key]
|
||||
};
|
||||
});
|
||||
|
||||
const childScope = this.manager.dataSchema.getScope(
|
||||
`${child.id}-${child.type}-currentRow`
|
||||
);
|
||||
|
||||
if (childScope) {
|
||||
childScope?.setSchemas([
|
||||
{
|
||||
$id: `${child.id}-${child.type}-currentRow`,
|
||||
type: 'object',
|
||||
properties: itemsSchema
|
||||
}
|
||||
]);
|
||||
childScope.tag = `当前行记录 : ${node.type}`;
|
||||
}
|
||||
itemsSchema[key] = {
|
||||
...tmpProperties[key]
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
childSchame = {
|
||||
$id: childSchame.$id,
|
||||
type: childSchame.type,
|
||||
properties: {
|
||||
...cellProperties,
|
||||
items: childSchame.properties.rows,
|
||||
selectedItems: {
|
||||
...childSchame.properties.selectedItems,
|
||||
|
@ -9,7 +9,8 @@ import {
|
||||
defaultValue,
|
||||
getSchemaTpl,
|
||||
CodeEditor as AmisCodeEditor,
|
||||
RendererPluginEvent
|
||||
RendererPluginEvent,
|
||||
tipedLabel
|
||||
} from 'amis-editor-core';
|
||||
import {getEventControlConfig} from '../renderer/event-control/helper';
|
||||
|
||||
@ -75,6 +76,23 @@ const DEFAULT_EVENT_PARAMS = [
|
||||
}
|
||||
];
|
||||
|
||||
const chartDefaultConfig = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
type: 'line'
|
||||
}
|
||||
],
|
||||
backgroundColor: 'transparent'
|
||||
};
|
||||
|
||||
export class ChartPlugin extends BasePlugin {
|
||||
static id = 'ChartPlugin';
|
||||
// 关联渲染器名字
|
||||
@ -92,21 +110,7 @@ export class ChartPlugin extends BasePlugin {
|
||||
pluginIcon = 'chart-plugin';
|
||||
scaffold = {
|
||||
type: 'chart',
|
||||
config: {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
type: 'line'
|
||||
}
|
||||
]
|
||||
},
|
||||
config: chartDefaultConfig,
|
||||
replaceChartOption: true
|
||||
};
|
||||
previewSchema = {
|
||||
@ -192,116 +196,157 @@ export class ChartPlugin extends BasePlugin {
|
||||
return [
|
||||
getSchemaTpl('tabs', [
|
||||
{
|
||||
title: '常规',
|
||||
title: '属性',
|
||||
body: [
|
||||
getSchemaTpl('layout:originPosition', {value: 'left-top'}),
|
||||
getSchemaTpl('api', {
|
||||
label: '接口拉取',
|
||||
description:
|
||||
'接口可以返回配置,或者数据,建议返回数据可映射到 Echarts 配置中'
|
||||
}),
|
||||
getSchemaTpl('collapseGroup', [
|
||||
{
|
||||
title: '基本',
|
||||
body: [
|
||||
getSchemaTpl('layout:originPosition', {value: 'left-top'}),
|
||||
getSchemaTpl('name')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '数据设置',
|
||||
body: [
|
||||
/*
|
||||
{
|
||||
type: 'select',
|
||||
name: 'chartDataType',
|
||||
label: '数据获取方式',
|
||||
value: 'json',
|
||||
onChange(value: any, oldValue: any, model: any, form: any) {
|
||||
if (value === 'json') {
|
||||
form.setValueByName('api', undefined);
|
||||
form.setValueByName('config', chartDefaultConfig);
|
||||
} else {
|
||||
form.setValueByName('config', undefined);
|
||||
}
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: '接口数据',
|
||||
value: 'dataApi'
|
||||
},
|
||||
{
|
||||
label: '静态JSON数据',
|
||||
value: 'json'
|
||||
}
|
||||
]
|
||||
},
|
||||
*/
|
||||
getSchemaTpl('apiControl', {
|
||||
label: '数据接口',
|
||||
// visibleOn: 'chartDataType === "dataApi"',
|
||||
description:
|
||||
'接口可以返回echart图表完整配置,或者图表数据,建议返回图表数据映射到 Echarts 配置中'
|
||||
}),
|
||||
|
||||
getSchemaTpl('switch', {
|
||||
label: '初始是否拉取',
|
||||
name: 'initFetch',
|
||||
visibleOn: 'data.api',
|
||||
pipeIn: defaultValue(true)
|
||||
}),
|
||||
getSchemaTpl('switch', {
|
||||
label: '初始是否拉取',
|
||||
name: 'initFetch',
|
||||
// visibleOn: 'chartDataType === "dataApi" && data.api',
|
||||
visibleOn: 'data.api',
|
||||
pipeIn: defaultValue(true)
|
||||
}),
|
||||
|
||||
{
|
||||
name: 'interval',
|
||||
label: '定时刷新间隔',
|
||||
type: 'input-number',
|
||||
step: 500,
|
||||
visibleOn: 'data.api',
|
||||
description: '设置后将自动定时刷新,最小3000, 单位 ms'
|
||||
},
|
||||
{
|
||||
name: 'config',
|
||||
asFormItem: true,
|
||||
component: ChartConfigEditor,
|
||||
// type: 'json-editor',
|
||||
label: 'Echarts 配置',
|
||||
description: '支持数据映射,可将接口返回的数据填充进来'
|
||||
// size: 'lg'
|
||||
// pipeOut: (value: any) => {
|
||||
// try {
|
||||
// return value ? JSON.parse(value) : null;
|
||||
// } catch (e) {}
|
||||
// return null;
|
||||
// }
|
||||
},
|
||||
{
|
||||
name: 'clickAction',
|
||||
asFormItem: true,
|
||||
children: ({onChange, value}: any) => (
|
||||
<div className="m-b">
|
||||
<Button
|
||||
size="sm"
|
||||
level={value ? 'danger' : 'info'}
|
||||
onClick={this.editDrillDown.bind(this, context.id)}
|
||||
>
|
||||
配置 DrillDown
|
||||
</Button>
|
||||
{
|
||||
name: 'interval',
|
||||
label: '定时刷新间隔',
|
||||
type: 'input-number',
|
||||
step: 500,
|
||||
// visibleOn: 'chartDataType === "dataApi" && data.api',
|
||||
visibleOn: 'data.api',
|
||||
description: '设置后将自动定时刷新,最小3000, 单位 ms'
|
||||
},
|
||||
{
|
||||
name: 'config',
|
||||
asFormItem: true,
|
||||
// visibleOn: 'chartDataType === "json"',
|
||||
component: ChartConfigEditor,
|
||||
// type: 'json-editor',
|
||||
label: tipedLabel(
|
||||
'Echarts 配置',
|
||||
'支持数据映射,可将接口返回的数据填充进来'
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'dataFilter',
|
||||
type: 'js-editor',
|
||||
allowFullscreen: true,
|
||||
label: '数据映射(dataFilter)',
|
||||
size: 'lg',
|
||||
description: `
|
||||
如果后端没有直接返回 Echart 配置,可以自己写一段函数来包装。
|
||||
<p>签名:(config, echarts, data) => config</p>
|
||||
<p>参数说明</p>
|
||||
<ul>
|
||||
<li><code>config</code> 原始数据</li>
|
||||
<li><code>echarts</code> echarts 对象</li>
|
||||
<li><code>data</code> 如果配置了数据接口,接口返回的数据通过此变量传入</li>
|
||||
</ul>
|
||||
<p>示例</p>
|
||||
<pre>debugger; // 可以浏览器中断点调试\n\n// 查看原始数据\nconsole.log(config)\n\n// 返回新的结果 \nreturn {}</pre>
|
||||
`
|
||||
},
|
||||
getSchemaTpl('switch', {
|
||||
label: 'Chart 配置完全替换',
|
||||
name: 'replaceChartOption',
|
||||
labelRemark: {
|
||||
trigger: 'click',
|
||||
className: 'm-l-xs',
|
||||
rootClose: true,
|
||||
content:
|
||||
'默认为追加模式,新的配置会跟旧的配置合并,如果勾选将直接完全覆盖。',
|
||||
placement: 'left'
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '图表下钻',
|
||||
body: [
|
||||
{
|
||||
name: 'clickAction',
|
||||
asFormItem: true,
|
||||
children: ({onChange, value}: any) => (
|
||||
<div className="m-b">
|
||||
<Button
|
||||
size="sm"
|
||||
level={value ? 'danger' : 'info'}
|
||||
onClick={this.editDrillDown.bind(this, context.id)}
|
||||
>
|
||||
配置 DrillDown
|
||||
</Button>
|
||||
|
||||
{value ? (
|
||||
<Button
|
||||
size="sm"
|
||||
level="link"
|
||||
className="m-l"
|
||||
onClick={() => onChange('')}
|
||||
>
|
||||
删除 DrillDown
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'dataFilter',
|
||||
type: 'js-editor',
|
||||
allowFullscreen: true,
|
||||
label: '数据加工',
|
||||
size: 'lg',
|
||||
description: `
|
||||
如果后端没有直接返回 Echart 配置,可以自己写一段函数来包装。
|
||||
<p>签名:(config, echarts, data) => config</p>
|
||||
<p>参数说明</p>
|
||||
<ul>
|
||||
<li><code>config</code> 原始数据</li>
|
||||
<li><code>echarts</code> echarts 对象</li>
|
||||
<li><code>data</code> 如果配置了数据接口,接口返回的数据通过此变量传入</li>
|
||||
</ul>
|
||||
<p>示例</p>
|
||||
<pre>debugger; // 可以浏览器中断点调试\n\n// 查看原始数据\nconsole.log(config)\n\n// 返回新的结果 \nreturn {}</pre>
|
||||
`
|
||||
},
|
||||
|
||||
getSchemaTpl('switch', {
|
||||
label: 'Chart 配置完全替换',
|
||||
name: 'replaceChartOption',
|
||||
labelRemark: {
|
||||
trigger: 'click',
|
||||
className: 'm-l-xs',
|
||||
rootClose: true,
|
||||
content:
|
||||
'默认为追加模式,新的配置会跟旧的配置合并,如果勾选将直接完全覆盖。',
|
||||
placement: 'left'
|
||||
}
|
||||
})
|
||||
{value ? (
|
||||
<Button
|
||||
size="sm"
|
||||
level="link"
|
||||
className="m-l"
|
||||
onClick={() => onChange('')}
|
||||
>
|
||||
删除 DrillDown
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('status')
|
||||
])
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '外观',
|
||||
body: [getSchemaTpl('className')]
|
||||
},
|
||||
{
|
||||
title: '显隐',
|
||||
body: [getSchemaTpl('visible')]
|
||||
},
|
||||
{
|
||||
title: '其他',
|
||||
body: [getSchemaTpl('name')]
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']}),
|
||||
{
|
||||
title: '自定义 CSS 类名',
|
||||
body: [getSchemaTpl('className')]
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
title: '事件',
|
||||
|
@ -204,34 +204,30 @@ export class ContainerPlugin extends LayoutBasePlugin {
|
||||
// 自由容器不需要 display 相关配置项
|
||||
...(!isFreeContainer ? displayTpl : []),
|
||||
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex', {
|
||||
isFlexColumnItem,
|
||||
label: isFlexColumnItem ? '高度设置' : '宽度设置',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative")'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-grow', {
|
||||
visibleOn:
|
||||
'data.style && data.style.flex === "1 1 auto" && (data.style.position === "static" || data.style.position === "relative")'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '弹性高度' : '弹性宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "1 1 auto"'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '固定高度' : '固定宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "0 0 150px"'
|
||||
})
|
||||
: null,
|
||||
...(isFlexItem
|
||||
? [
|
||||
getSchemaTpl('layout:flex', {
|
||||
isFlexColumnItem,
|
||||
label: isFlexColumnItem ? '高度设置' : '宽度设置',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative")'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-grow', {
|
||||
visibleOn:
|
||||
'data.style && data.style.flex === "1 1 auto" && (data.style.position === "static" || data.style.position === "relative")'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '弹性高度' : '弹性宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "1 1 auto"'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '固定高度' : '固定宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "0 0 150px"'
|
||||
})
|
||||
]
|
||||
: []),
|
||||
|
||||
getSchemaTpl('layout:overflow-x', {
|
||||
visibleOn: `${
|
||||
@ -308,7 +304,11 @@ export class ContainerPlugin extends LayoutBasePlugin {
|
||||
title: '外观',
|
||||
className: 'p-none',
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']})
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']}),
|
||||
{
|
||||
title: '自定义 CSS 类名',
|
||||
body: [getSchemaTpl('className')]
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
} from 'amis-editor-core';
|
||||
import {getEventControlConfig} from '../renderer/event-control/helper';
|
||||
import omit from 'lodash/omit';
|
||||
import type {RendererConfig, Schema} from 'amis-core';
|
||||
|
||||
export class DialogPlugin extends BasePlugin {
|
||||
static id = 'DialogPlugin';
|
||||
@ -349,6 +350,35 @@ export class DialogPlugin extends BasePlugin {
|
||||
properties: dataSchema
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 为了让 dialog 的按钮可以点击编辑
|
||||
*/
|
||||
patchSchema(schema: Schema, info: RendererConfig, props?: any) {
|
||||
if (Array.isArray(schema.actions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
...schema,
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
actionType: 'cancel',
|
||||
label: '取消'
|
||||
},
|
||||
|
||||
props?.confirm
|
||||
? {
|
||||
type: 'button',
|
||||
actionType: 'confirm',
|
||||
label: '确定',
|
||||
primary: true
|
||||
}
|
||||
: null
|
||||
].filter((item: any) => item)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorPlugin(DialogPlugin);
|
||||
|
@ -664,16 +664,41 @@ export class ComboControlPlugin extends BasePlugin {
|
||||
async buildDataSchemas(
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
trigger?: EditorNodeType,
|
||||
parent?: EditorNodeType
|
||||
) {
|
||||
const itemsSchema: any = {
|
||||
$id: 'comboItems',
|
||||
$id: `${node.id}-${node.type}-tableRows`,
|
||||
type: 'object',
|
||||
properties: {}
|
||||
};
|
||||
const items = node.children?.find(
|
||||
child => child.isRegion && child.region === 'items'
|
||||
);
|
||||
const parentScopeId = `${parent?.id}-${parent?.type}${
|
||||
node.parent?.type === 'cell' ? '-currentRow' : ''
|
||||
}`;
|
||||
let isColumnChild = false;
|
||||
|
||||
if (trigger) {
|
||||
isColumnChild = someTree(items.children, item => item.id === trigger?.id);
|
||||
|
||||
if (isColumnChild) {
|
||||
const scopeId = `${node.id}-${node.type}-currentRow`;
|
||||
if (this.manager.dataSchema.getScope(scopeId)) {
|
||||
this.manager.dataSchema.removeScope(scopeId);
|
||||
}
|
||||
|
||||
if (this.manager.dataSchema.getScope(parentScopeId)) {
|
||||
this.manager.dataSchema.switchTo(parentScopeId);
|
||||
}
|
||||
|
||||
this.manager.dataSchema.addScope([], scopeId);
|
||||
this.manager.dataSchema.current.tag = '当前行记录';
|
||||
this.manager.dataSchema.current.group = '组件上下文';
|
||||
}
|
||||
}
|
||||
|
||||
const pool = items.children.concat();
|
||||
|
||||
while (pool.length) {
|
||||
@ -681,16 +706,22 @@ export class ComboControlPlugin extends BasePlugin {
|
||||
const schema = current.schema;
|
||||
|
||||
if (schema.name) {
|
||||
itemsSchema.properties[schema.name] = current.info?.plugin
|
||||
?.buildDataSchemas
|
||||
? await current.info.plugin.buildDataSchemas(current, region, trigger)
|
||||
: {
|
||||
type: 'string',
|
||||
title: schema.label || schema.name
|
||||
};
|
||||
itemsSchema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
region,
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isColumnChild) {
|
||||
const scopeId = `${node.id}-${node.type}-currentRow`;
|
||||
const scope = this.manager.dataSchema.getScope(scopeId);
|
||||
scope?.addSchema(itemsSchema);
|
||||
}
|
||||
|
||||
if (node.schema?.multiple) {
|
||||
return {
|
||||
$id: 'combo',
|
||||
@ -700,7 +731,10 @@ export class ComboControlPlugin extends BasePlugin {
|
||||
};
|
||||
}
|
||||
|
||||
return {...itemsSchema, title: node.schema?.label || node.schema?.name};
|
||||
return {
|
||||
...itemsSchema,
|
||||
title: node.schema?.label || node.schema?.name
|
||||
};
|
||||
}
|
||||
|
||||
async getAvailableContextFields(
|
||||
|
@ -1,4 +1,8 @@
|
||||
import {getI18nEnabled, registerEditorPlugin} from 'amis-editor-core';
|
||||
import {
|
||||
JSONPipeOut,
|
||||
getI18nEnabled,
|
||||
registerEditorPlugin
|
||||
} from 'amis-editor-core';
|
||||
import {
|
||||
BasePlugin,
|
||||
ChangeEventContext,
|
||||
@ -11,7 +15,7 @@ import {defaultValue, getSchemaTpl} from 'amis-editor-core';
|
||||
import {jsonToJsonSchema} from 'amis-editor-core';
|
||||
import {EditorNodeType} from 'amis-editor-core';
|
||||
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
|
||||
import {setVariable, someTree} from 'amis-core';
|
||||
import {RendererConfig, Schema, setVariable, someTree} from 'amis-core';
|
||||
import {getEventControlConfig} from '../../renderer/event-control/helper';
|
||||
|
||||
// 用于脚手架的常用表单控件
|
||||
@ -939,102 +943,22 @@ export class FormPlugin extends BasePlugin {
|
||||
trigger?: EditorNodeType
|
||||
) {
|
||||
const jsonschema: any = {
|
||||
$id: 'formItems',
|
||||
type: 'object',
|
||||
properties: {}
|
||||
...jsonToJsonSchema(JSONPipeOut(node.schema.data))
|
||||
};
|
||||
|
||||
const pool = node.children.concat();
|
||||
|
||||
while (pool.length) {
|
||||
const current = pool.shift() as EditorNodeType;
|
||||
const schema = current.schema;
|
||||
|
||||
if (current.rendererConfig?.type === 'combo') {
|
||||
if (trigger) {
|
||||
const items = current.children?.find(
|
||||
child => child.isRegion && child.region === 'items'
|
||||
if (current.rendererConfig?.isFormItem && schema.name) {
|
||||
jsonschema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
region,
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
const isItemsChild = someTree(
|
||||
items.children,
|
||||
item => item.id === trigger?.id
|
||||
);
|
||||
|
||||
if (isItemsChild) {
|
||||
const itemsChilds = items.children.concat();
|
||||
|
||||
while (itemsChilds.length) {
|
||||
const currentItem = itemsChilds.shift() as EditorNodeType;
|
||||
const itemSchema = currentItem.schema;
|
||||
|
||||
if (itemSchema.name) {
|
||||
jsonschema.properties[itemSchema.name] = {
|
||||
...(currentItem.info?.plugin?.buildDataSchemas
|
||||
? await currentItem.info.plugin.buildDataSchemas(
|
||||
currentItem,
|
||||
region,
|
||||
trigger
|
||||
)
|
||||
: {
|
||||
type: 'string',
|
||||
title: itemSchema.label || itemSchema.name
|
||||
}),
|
||||
group: `当前行记录(${schema.label || schema.name})`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.name) {
|
||||
jsonschema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(current, region);
|
||||
}
|
||||
} else if (current.rendererConfig?.type === 'input-table') {
|
||||
if (trigger) {
|
||||
const columns: EditorNodeType = current.children.find(
|
||||
item => item.isRegion && item.region === 'columns'
|
||||
);
|
||||
const isColumnChild = someTree(
|
||||
columns?.children,
|
||||
item => item.id === trigger.id
|
||||
);
|
||||
|
||||
if (isColumnChild) {
|
||||
for (let col of schema?.columns) {
|
||||
if (col.name) {
|
||||
jsonschema.properties[col.name] = {
|
||||
type: 'string',
|
||||
title: col.label || col.name,
|
||||
group: `当前行记录(${schema.label || schema.name})`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (schema.name) {
|
||||
jsonschema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
region,
|
||||
trigger
|
||||
);
|
||||
}
|
||||
} else if (current.rendererConfig?.isFormItem) {
|
||||
if (schema.name) {
|
||||
jsonschema.properties[schema.name] = current.info?.plugin
|
||||
?.buildDataSchemas
|
||||
? await current.info.plugin.buildDataSchemas(
|
||||
current,
|
||||
region,
|
||||
trigger
|
||||
)
|
||||
: {
|
||||
type: 'string',
|
||||
title:
|
||||
typeof schema.label === 'string' ? schema.label : schema.name,
|
||||
originalValue: schema.value // 记录原始值,循环引用检测需要
|
||||
};
|
||||
}
|
||||
} else {
|
||||
pool.push(...current.children);
|
||||
}
|
||||
@ -1056,6 +980,40 @@ export class FormPlugin extends BasePlugin {
|
||||
scope?.addSchema(jsonschema);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为了让 form 的按钮可以点击编辑
|
||||
*/
|
||||
patchSchema(schema: Schema, info: RendererConfig, props: any) {
|
||||
if (
|
||||
Array.isArray(schema.actions) ||
|
||||
schema.wrapWithPanel === false ||
|
||||
(Array.isArray(schema.body) &&
|
||||
schema.body.some(
|
||||
(item: any) =>
|
||||
item &&
|
||||
!!~['submit', 'button', 'button-group', 'reset'].indexOf(
|
||||
(item as any)?.body?.[0]?.type ||
|
||||
(item as any)?.body?.type ||
|
||||
(item as any).type
|
||||
)
|
||||
))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
...schema,
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label:
|
||||
props?.translate(props?.submitText) || schema.submitText || '提交',
|
||||
primary: true
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorPlugin(FormPlugin);
|
||||
|
@ -325,6 +325,9 @@ export class DateRangeControlPlugin extends BasePlugin {
|
||||
getSchemaTpl('formItemName', {
|
||||
required: true
|
||||
}),
|
||||
|
||||
getSchemaTpl('formItemExtraName'),
|
||||
|
||||
getSchemaTpl('label'),
|
||||
getSchemaTpl('selectDateRangeType', {
|
||||
value: this.scaffold.type,
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
EditorManager,
|
||||
DSBuilderManager
|
||||
} from 'amis-editor-core';
|
||||
import {setVariable, someTree} from 'amis-core';
|
||||
import {getTreeAncestors, setVariable, someTree} from 'amis-core';
|
||||
import {ValidatorTag} from '../../validator';
|
||||
import {
|
||||
getEventControlConfig,
|
||||
@ -1105,20 +1105,46 @@ export class TableControlPlugin extends BasePlugin {
|
||||
async buildDataSchemas(
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
trigger?: EditorNodeType,
|
||||
parent?: EditorNodeType
|
||||
) {
|
||||
const itemsSchema: any = {
|
||||
$id: 'inputTableRow',
|
||||
$id: `${node.id}-${node.type}-tableRows`,
|
||||
type: 'object',
|
||||
properties: {}
|
||||
};
|
||||
|
||||
const columns: EditorNodeType = node.children.find(
|
||||
item => item.isRegion && item.region === 'columns'
|
||||
);
|
||||
const parentScopeId = `${parent?.id}-${parent?.type}${
|
||||
node.parent?.type === 'cell' ? '-currentRow' : ''
|
||||
}`;
|
||||
let isColumnChild = false;
|
||||
|
||||
// 追加当前行scope
|
||||
if (trigger) {
|
||||
isColumnChild = someTree(
|
||||
columns?.children,
|
||||
item => item.id === trigger.id
|
||||
);
|
||||
|
||||
if (isColumnChild) {
|
||||
const scopeId = `${node.id}-${node.type}-currentRow`;
|
||||
if (this.manager.dataSchema.getScope(scopeId)) {
|
||||
this.manager.dataSchema.removeScope(scopeId);
|
||||
}
|
||||
|
||||
if (this.manager.dataSchema.getScope(parentScopeId)) {
|
||||
this.manager.dataSchema.switchTo(parentScopeId);
|
||||
}
|
||||
|
||||
this.manager.dataSchema.addScope([], scopeId);
|
||||
this.manager.dataSchema.current.tag = '当前行记录';
|
||||
this.manager.dataSchema.current.group = '组件上下文';
|
||||
}
|
||||
}
|
||||
|
||||
const cells: any = columns.children.concat();
|
||||
|
||||
while (cells.length > 0) {
|
||||
const cell = cells.shift() as EditorNodeType;
|
||||
// cell的孩子貌似只会有一个
|
||||
@ -1128,17 +1154,13 @@ export class TableControlPlugin extends BasePlugin {
|
||||
const schema = current.schema;
|
||||
|
||||
if (schema.name) {
|
||||
itemsSchema.properties[schema.name] = current.info?.plugin
|
||||
?.buildDataSchemas
|
||||
? await current.info.plugin.buildDataSchemas(
|
||||
current,
|
||||
region,
|
||||
trigger
|
||||
)
|
||||
: {
|
||||
type: 'string',
|
||||
title: schema.label || schema.name
|
||||
};
|
||||
itemsSchema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
region,
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1147,8 +1169,15 @@ export class TableControlPlugin extends BasePlugin {
|
||||
return itemsSchema;
|
||||
}
|
||||
|
||||
// 追加当前行数据
|
||||
if (isColumnChild) {
|
||||
const scopeId = `${node.id}-${node.type}-currentRow`;
|
||||
const scope = this.manager.dataSchema.getScope(scopeId);
|
||||
scope?.addSchema(itemsSchema);
|
||||
}
|
||||
|
||||
return {
|
||||
$id: 'inputTable',
|
||||
$id: `${node.id}-${node.type}-tableData`,
|
||||
type: 'array',
|
||||
title: node.schema?.label || node.schema?.name,
|
||||
items: itemsSchema
|
||||
|
@ -1,16 +1,15 @@
|
||||
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
|
||||
import {registerEditorPlugin} from 'amis-editor-core';
|
||||
import {
|
||||
registerEditorPlugin,
|
||||
BasePlugin,
|
||||
BasicSubRenderInfo,
|
||||
RendererEventContext,
|
||||
SubRendererInfo,
|
||||
BaseEventContext,
|
||||
tipedLabel
|
||||
RendererPluginAction,
|
||||
RendererPluginEvent,
|
||||
tipedLabel,
|
||||
defaultValue,
|
||||
getSchemaTpl
|
||||
} from 'amis-editor-core';
|
||||
import {ValidatorTag} from '../../validator';
|
||||
import {getEventControlConfig} from '../../renderer/event-control/helper';
|
||||
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
|
||||
|
||||
export class MatrixControlPlugin extends BasePlugin {
|
||||
static id = 'MatrixControlPlugin';
|
||||
@ -127,8 +126,9 @@ export class MatrixControlPlugin extends BasePlugin {
|
||||
required: true
|
||||
}),
|
||||
getSchemaTpl('label'),
|
||||
getSchemaTpl('multiple', {
|
||||
value: true
|
||||
getSchemaTpl('switch', {
|
||||
name: 'multiple',
|
||||
label: '可多选'
|
||||
}),
|
||||
{
|
||||
label: tipedLabel('模式', '行级、列级或者单个单元单选'),
|
||||
@ -140,7 +140,7 @@ export class MatrixControlPlugin extends BasePlugin {
|
||||
left: 2,
|
||||
justify: true
|
||||
},
|
||||
visibleOn: '!this.multiple',
|
||||
visibleOn: '!data.multiple',
|
||||
options: [
|
||||
{
|
||||
label: '行级',
|
||||
@ -157,37 +157,15 @@ export class MatrixControlPlugin extends BasePlugin {
|
||||
],
|
||||
pipeIn: defaultValue('column')
|
||||
},
|
||||
getSchemaTpl('autoFillApi'),
|
||||
{
|
||||
label: tipedLabel('列全选', '列级全选功能'),
|
||||
getSchemaTpl('switch', {
|
||||
name: 'yCheckAll',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '是',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: tipedLabel('行全选', '行级全选功能'),
|
||||
label: tipedLabel('列全选', '列级全选功能')
|
||||
}),
|
||||
getSchemaTpl('switch', {
|
||||
name: 'xCheckAll',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '是',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: false
|
||||
}
|
||||
]
|
||||
}
|
||||
label: tipedLabel('行全选', '行级全选功能')
|
||||
}),
|
||||
getSchemaTpl('autoFillApi')
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -113,34 +113,30 @@ export class FlexPluginBase extends LayoutBasePlugin {
|
||||
|
||||
getSchemaTpl('layout:flex-wrap'),
|
||||
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex', {
|
||||
isFlexColumnItem,
|
||||
label: isFlexColumnItem ? '高度设置' : '宽度设置',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative")'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-grow', {
|
||||
visibleOn:
|
||||
'data.style && data.style.flex === "1 1 auto" && (data.style.position === "static" || data.style.position === "relative")'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '弹性高度' : '弹性宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "1 1 auto"'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '固定高度' : '固定宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "0 0 150px"'
|
||||
})
|
||||
: null,
|
||||
...(isFlexItem
|
||||
? [
|
||||
getSchemaTpl('layout:flex', {
|
||||
isFlexColumnItem,
|
||||
label: isFlexColumnItem ? '高度设置' : '宽度设置',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative")'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-grow', {
|
||||
visibleOn:
|
||||
'data.style && data.style.flex === "1 1 auto" && (data.style.position === "static" || data.style.position === "relative")'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '弹性高度' : '弹性宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "1 1 auto"'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '固定高度' : '固定宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "0 0 150px"'
|
||||
})
|
||||
]
|
||||
: []),
|
||||
|
||||
getSchemaTpl('layout:overflow-x', {
|
||||
visibleOn: `${
|
||||
@ -212,7 +208,11 @@ export class FlexPluginBase extends LayoutBasePlugin {
|
||||
title: '外观',
|
||||
className: 'p-none',
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']})
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']}),
|
||||
{
|
||||
title: '自定义 CSS 类名',
|
||||
body: [getSchemaTpl('className')]
|
||||
}
|
||||
])
|
||||
}
|
||||
])
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {ContainerWrapper} from 'amis-editor-core';
|
||||
import {ContainerWrapper, JSONPipeOut} from 'amis-editor-core';
|
||||
import {registerEditorPlugin} from 'amis-editor-core';
|
||||
import {
|
||||
BaseEventContext,
|
||||
@ -369,14 +369,30 @@ export class PagePlugin extends BasePlugin {
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
) {
|
||||
const scope = this.manager.dataSchema.getScope(`${node.id}-${node.type}`);
|
||||
let jsonschema = {
|
||||
$id: 'pageStaticData',
|
||||
...jsonToJsonSchema(omit(node.schema.data, '$$id'))
|
||||
...jsonToJsonSchema(JSONPipeOut(node.schema.data))
|
||||
};
|
||||
|
||||
scope?.removeSchema(jsonschema.$id);
|
||||
scope?.addSchema(jsonschema);
|
||||
const pool = node.children.concat();
|
||||
|
||||
while (pool.length) {
|
||||
const current = pool.shift() as EditorNodeType;
|
||||
const schema = current.schema;
|
||||
|
||||
if (current.rendererConfig?.isFormItem && schema?.name) {
|
||||
jsonschema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
undefined,
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
} else if (!current.rendererConfig?.storeType) {
|
||||
pool.push(...current.children);
|
||||
}
|
||||
}
|
||||
|
||||
return jsonschema;
|
||||
}
|
||||
|
||||
rendererBeforeDispatchEvent(node: EditorNodeType, e: any, data: any) {
|
||||
|
@ -2,6 +2,7 @@ import {Button} from 'amis';
|
||||
import React from 'react';
|
||||
import {
|
||||
EditorNodeType,
|
||||
JSONPipeOut,
|
||||
jsonToJsonSchema,
|
||||
registerEditorPlugin
|
||||
} from 'amis-editor-core';
|
||||
@ -151,9 +152,6 @@ export class ServicePlugin extends BasePlugin {
|
||||
panelTitle = '服务';
|
||||
|
||||
panelBodyCreator = (context: BaseEventContext) => {
|
||||
console.log(context);
|
||||
console.log(context.node.parent);
|
||||
console.log(context.node.parent.getComponent());
|
||||
return getSchemaTpl('tabs', [
|
||||
{
|
||||
title: '属性',
|
||||
@ -291,6 +289,36 @@ export class ServicePlugin extends BasePlugin {
|
||||
]);
|
||||
};
|
||||
|
||||
async buildDataSchemas(
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
) {
|
||||
let jsonschema: any = {
|
||||
...jsonToJsonSchema(JSONPipeOut(node.schema.data ?? {}))
|
||||
};
|
||||
const pool = node.children.concat();
|
||||
|
||||
while (pool.length) {
|
||||
const current = pool.shift() as EditorNodeType;
|
||||
const schema = current.schema;
|
||||
|
||||
if (current.rendererConfig?.isFormItem && schema?.name) {
|
||||
jsonschema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
undefined,
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
} else if (!current.rendererConfig?.storeType) {
|
||||
pool.push(...current.children);
|
||||
}
|
||||
}
|
||||
|
||||
return jsonschema;
|
||||
}
|
||||
|
||||
rendererBeforeDispatchEvent(node: EditorNodeType, e: any, data: any) {
|
||||
if (e === 'fetchInited') {
|
||||
const scope = this.manager.dataSchema.getScope(`${node.id}-${node.type}`);
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import {registerEditorPlugin} from 'amis-editor-core';
|
||||
import {BasePlugin} from 'amis-editor-core';
|
||||
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
|
||||
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
|
||||
|
||||
export class SparklinePlugin extends BasePlugin {
|
||||
@ -30,14 +30,54 @@ export class SparklinePlugin extends BasePlugin {
|
||||
};
|
||||
|
||||
panelTitle = '走势图';
|
||||
panelBody = [
|
||||
getSchemaTpl('layout:originPosition', {value: 'left-top'}),
|
||||
{
|
||||
name: 'height',
|
||||
type: 'input-number',
|
||||
label: '高度'
|
||||
}
|
||||
];
|
||||
|
||||
panelJustify = true;
|
||||
panelBodyCreator = (context: BaseEventContext) => {
|
||||
return [
|
||||
getSchemaTpl('tabs', [
|
||||
{
|
||||
title: '属性',
|
||||
body: [
|
||||
getSchemaTpl('collapseGroup', [
|
||||
{
|
||||
title: '基本',
|
||||
body: [
|
||||
getSchemaTpl('layout:originPosition', {value: 'left-top'}),
|
||||
getSchemaTpl('name')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '宽高设置',
|
||||
body: [
|
||||
{
|
||||
name: 'width',
|
||||
type: 'input-number',
|
||||
label: '宽度'
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
type: 'input-number',
|
||||
label: '高度'
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('status')
|
||||
])
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '外观',
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']}),
|
||||
{
|
||||
title: '自定义 CSS 类名',
|
||||
body: [getSchemaTpl('className')]
|
||||
}
|
||||
])
|
||||
}
|
||||
])
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
registerEditorPlugin(SparklinePlugin);
|
||||
|
@ -760,40 +760,66 @@ export class TablePlugin extends BasePlugin {
|
||||
async buildDataSchemas(
|
||||
node: EditorNodeType,
|
||||
region?: EditorNodeType,
|
||||
trigger?: EditorNodeType
|
||||
trigger?: EditorNodeType,
|
||||
parent?: EditorNodeType
|
||||
) {
|
||||
let itemsSchema: any = {
|
||||
$id: 'tableRow',
|
||||
$id: `${node.id}-${node.type}-tableRows`,
|
||||
type: 'object',
|
||||
properties: {}
|
||||
};
|
||||
const columns: EditorNodeType = node.children.find(
|
||||
item => item.isRegion && item.region === 'columns'
|
||||
);
|
||||
const cells: any = columns.children.concat();
|
||||
const parentScopeId = `${parent?.id}-${parent?.type}${
|
||||
node.parent?.type === 'cell' ? '-currentRow' : ''
|
||||
}`;
|
||||
let isColumnChild = false;
|
||||
|
||||
while (cells.length > 0 && cells.length <= columns.children?.length) {
|
||||
// 追加当前行scope
|
||||
if (trigger) {
|
||||
isColumnChild = someTree(
|
||||
columns?.children,
|
||||
item => item.id === trigger.id
|
||||
);
|
||||
|
||||
if (isColumnChild) {
|
||||
const scopeId = `${node.id}-${node.type}-currentRow`;
|
||||
if (this.manager.dataSchema.getScope(scopeId)) {
|
||||
this.manager.dataSchema.removeScope(scopeId);
|
||||
}
|
||||
|
||||
if (this.manager.dataSchema.getScope(parentScopeId)) {
|
||||
this.manager.dataSchema.switchTo(parentScopeId);
|
||||
}
|
||||
|
||||
this.manager.dataSchema.addScope([], scopeId);
|
||||
this.manager.dataSchema.current.tag = '当前行记录';
|
||||
this.manager.dataSchema.current.group = '组件上下文';
|
||||
}
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
const cells: any = columns.children.concat();
|
||||
// 存在预览节点,限制下遍历数
|
||||
while (cells.length > 0 && index < node.schema.columns.length) {
|
||||
const cell = cells.shift() as EditorNodeType;
|
||||
// cell的孩子貌似只会有一个
|
||||
const items = cell.children.concat();
|
||||
while (items.length) {
|
||||
const current = items.shift() as EditorNodeType;
|
||||
const schema = current.schema;
|
||||
|
||||
if (schema.name) {
|
||||
itemsSchema.properties[schema.name] = current.info?.plugin
|
||||
?.buildDataSchemas
|
||||
? await current.info.plugin.buildDataSchemas(
|
||||
current,
|
||||
region,
|
||||
trigger
|
||||
)
|
||||
: {
|
||||
type: 'string',
|
||||
title: schema.label || schema.name
|
||||
};
|
||||
itemsSchema.properties[schema.name] =
|
||||
await current.info.plugin.buildDataSchemas?.(
|
||||
current,
|
||||
region,
|
||||
trigger,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// 收集source绑定的列表成员
|
||||
@ -831,28 +857,17 @@ export class TablePlugin extends BasePlugin {
|
||||
return itemsSchema;
|
||||
}
|
||||
|
||||
let cellProperties: any = {};
|
||||
if (trigger) {
|
||||
const isColumnChild = someTree(
|
||||
columns?.children,
|
||||
item => item.id === trigger.id
|
||||
);
|
||||
|
||||
if (isColumnChild) {
|
||||
Object.keys(itemsSchema.properties).map(key => {
|
||||
cellProperties[key] = {
|
||||
...itemsSchema.properties[key],
|
||||
group: '当前行记录'
|
||||
};
|
||||
});
|
||||
}
|
||||
// 追加当前行数据
|
||||
if (isColumnChild) {
|
||||
const scopeId = `${node.id}-${node.type}-currentRow`;
|
||||
const scope = this.manager.dataSchema.getScope(scopeId);
|
||||
scope?.addSchema(itemsSchema);
|
||||
}
|
||||
|
||||
return {
|
||||
$id: 'table',
|
||||
$id: `${node.id}-${node.type}`,
|
||||
type: 'object',
|
||||
properties: {
|
||||
...cellProperties,
|
||||
rows: {
|
||||
type: 'array',
|
||||
title: '数据列表',
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import {getEventControlConfig} from '../renderer/event-control/helper';
|
||||
import {tipedLabel} from 'amis-editor-core';
|
||||
import {registerEditorPlugin, getSchemaTpl} from 'amis-editor-core';
|
||||
import {registerEditorPlugin, getSchemaTpl, diff} from 'amis-editor-core';
|
||||
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
|
||||
import {schemaArrayFormat, schemaToArray} from '../util';
|
||||
|
||||
export class TimelinePlugin extends BasePlugin {
|
||||
static id = 'TimelinePlugin';
|
||||
@ -90,7 +91,42 @@ export class TimelinePlugin extends BasePlugin {
|
||||
getSchemaTpl('timelineItemControl', {
|
||||
name: 'items',
|
||||
mode: 'normal'
|
||||
})
|
||||
}),
|
||||
{
|
||||
type: 'ae-switch-more',
|
||||
mode: 'normal',
|
||||
label: '自定义标题显示模板',
|
||||
bulk: false,
|
||||
name: 'itemTitleSchema',
|
||||
formType: 'extend',
|
||||
form: {
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
level: 'primary',
|
||||
size: 'sm',
|
||||
block: true,
|
||||
onClick: this.editDetail.bind(this, context),
|
||||
label: '配置标题显示模板'
|
||||
}
|
||||
]
|
||||
},
|
||||
pipeIn: (value: any) => {
|
||||
if (typeof value === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
return typeof value !== 'string';
|
||||
},
|
||||
pipeOut: (value: any) => {
|
||||
if (value === true) {
|
||||
return {
|
||||
type: 'tpl',
|
||||
tpl: '请编辑标题内容'
|
||||
};
|
||||
}
|
||||
return value ? value : undefined;
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('status')
|
||||
@ -105,6 +141,33 @@ export class TimelinePlugin extends BasePlugin {
|
||||
])
|
||||
}
|
||||
]);
|
||||
|
||||
editDetail(context: BaseEventContext) {
|
||||
const {id, schema} = context;
|
||||
const manager = this.manager;
|
||||
const store = manager.store;
|
||||
const node = store.getNodeById(id);
|
||||
const value = store.getValueOf(id);
|
||||
const defaultItemSchema = {
|
||||
type: 'tpl',
|
||||
tpl: '请编辑标题内容'
|
||||
};
|
||||
node &&
|
||||
value &&
|
||||
this.manager.openSubEditor({
|
||||
title: '配置标题显示模板',
|
||||
value: schemaToArray(value.itemTitleSchema ?? defaultItemSchema),
|
||||
slot: {
|
||||
type: 'container',
|
||||
body: '$$'
|
||||
},
|
||||
onChange: (newValue: any) => {
|
||||
newValue = {...value, itemTitleSchema: schemaArrayFormat(newValue)};
|
||||
manager.panelChangeValue(newValue, diff(value, newValue));
|
||||
},
|
||||
data: schema
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorPlugin(TimelinePlugin);
|
||||
|
@ -78,34 +78,30 @@ export class WrapperPlugin extends LayoutBasePlugin {
|
||||
'data.style && (data.style.display === "flex" || data.style.display === "inline-flex")'
|
||||
}),
|
||||
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex', {
|
||||
isFlexColumnItem,
|
||||
label: isFlexColumnItem ? '高度设置' : '宽度设置',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative")'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-grow', {
|
||||
visibleOn:
|
||||
'data.style && data.style.flex === "1 1 auto" && (data.style.position === "static" || data.style.position === "relative")'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '弹性高度' : '弹性宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "1 1 auto"'
|
||||
})
|
||||
: null,
|
||||
isFlexItem
|
||||
? getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '固定高度' : '固定宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "0 0 150px"'
|
||||
})
|
||||
: null,
|
||||
...(isFlexItem
|
||||
? [
|
||||
getSchemaTpl('layout:flex', {
|
||||
isFlexColumnItem,
|
||||
label: isFlexColumnItem ? '高度设置' : '宽度设置',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative")'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-grow', {
|
||||
visibleOn:
|
||||
'data.style && data.style.flex === "1 1 auto" && (data.style.position === "static" || data.style.position === "relative")'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '弹性高度' : '弹性宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "1 1 auto"'
|
||||
}),
|
||||
getSchemaTpl('layout:flex-basis', {
|
||||
label: isFlexColumnItem ? '固定高度' : '固定宽度',
|
||||
visibleOn:
|
||||
'data.style && (data.style.position === "static" || data.style.position === "relative") && data.style.flex === "0 0 150px"'
|
||||
})
|
||||
]
|
||||
: []),
|
||||
|
||||
getSchemaTpl('layout:overflow-x', {
|
||||
visibleOn: `${
|
||||
|
@ -79,6 +79,28 @@ setSchemaTpl('formItemName', {
|
||||
// validateOnChange: false
|
||||
});
|
||||
|
||||
setSchemaTpl('formItemExtraName', {
|
||||
className: 'mb-3',
|
||||
type: 'fieldset',
|
||||
body: [
|
||||
getSchemaTpl('formItemName', {
|
||||
required: true,
|
||||
label: '额外字段',
|
||||
name: 'extraName',
|
||||
visibleOn: 'typeof this.extraName === "string"'
|
||||
}),
|
||||
|
||||
{
|
||||
type: 'switch',
|
||||
label: tipedLabel('存成两个字段', '开启后将选中范围分别存成两个字段'),
|
||||
name: 'extraName',
|
||||
pipeIn: (value: any) => typeof value === 'string',
|
||||
pipeOut: (value: any) => (value ? '' : undefined),
|
||||
inputClassName: 'is-inline'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
setSchemaTpl(
|
||||
'formItemMode',
|
||||
(config: {
|
||||
|
@ -599,6 +599,9 @@ setSchemaTpl(
|
||||
// 适配
|
||||
form.setValueByName('style.flexGrow', undefined);
|
||||
form.setValueByName('style.flexBasis', undefined);
|
||||
form.setValueByName('style.overflowX', undefined);
|
||||
form.setValueByName('style.overflowY', undefined);
|
||||
form.setValueByName('style.overflow', undefined);
|
||||
|
||||
if (config?.isFlexColumnItem) {
|
||||
form.setValueByName('style.height', undefined);
|
||||
@ -893,7 +896,7 @@ setSchemaTpl(
|
||||
config?.label ||
|
||||
tipedLabel(' x轴滚动模式', '用于设置水平方向的滚动模式'),
|
||||
name: config?.name || 'style.overflowX',
|
||||
value: config?.value || 'auto',
|
||||
value: config?.value || 'visible',
|
||||
visibleOn: config?.visibleOn,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut,
|
||||
@ -1099,7 +1102,7 @@ setSchemaTpl(
|
||||
config?.label ||
|
||||
tipedLabel(' y轴滚动模式', '用于设置垂直方向的滚动模式'),
|
||||
name: config?.name || 'style.overflowY',
|
||||
value: config?.value || 'auto',
|
||||
value: config?.value || 'visible',
|
||||
visibleOn: config?.visibleOn,
|
||||
pipeIn: config?.pipeIn,
|
||||
pipeOut: config?.pipeOut,
|
||||
|
@ -64,7 +64,7 @@
|
||||
"react-hook-form": "7.39.0",
|
||||
"react-json-view": "1.21.3",
|
||||
"react-overlays": "5.1.1",
|
||||
"react-textarea-autosize": "8.5.2",
|
||||
"react-textarea-autosize": "8.3.3",
|
||||
"react-transition-group": "4.4.2",
|
||||
"react-visibility-sensor": "5.1.1",
|
||||
"sortablejs": "1.15.0",
|
||||
|
@ -279,6 +279,26 @@ $widths: map-merge(
|
||||
)
|
||||
) !default;
|
||||
|
||||
$minWidths: (
|
||||
none: none,
|
||||
0: 0rem,
|
||||
xs: 20rem,
|
||||
sm: 24rem,
|
||||
md: 28rem,
|
||||
lg: 32rem,
|
||||
xl: 36rem,
|
||||
2xl: 42rem,
|
||||
3xl: 48rem,
|
||||
4xl: 56rem,
|
||||
5xl: 64rem,
|
||||
6xl: 72rem,
|
||||
7xl: 80rem,
|
||||
full: 100%,
|
||||
min: min-content,
|
||||
max: max-content,
|
||||
prose: 65ch
|
||||
) !default;
|
||||
|
||||
$maxWidths: (
|
||||
none: none,
|
||||
0: 0rem,
|
||||
|
@ -282,7 +282,7 @@
|
||||
padding: 10px;
|
||||
margin: -10px 0px;
|
||||
background: $white;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
> .#{$ns}CBGroupOrItem-dragbar {
|
||||
left: px2rem(-5px);
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
&-tab {
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #3d3d3d;
|
||||
}
|
||||
|
||||
&-tab > button {
|
||||
@ -90,6 +91,7 @@
|
||||
&-content {
|
||||
pointer-events: all;
|
||||
display: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&-resize {
|
||||
@ -122,7 +124,7 @@
|
||||
|
||||
&.is-expanded {
|
||||
width: 420px;
|
||||
overflow: auto;
|
||||
|
||||
background: #272821;
|
||||
color: #cccccc;
|
||||
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
|
||||
@ -168,6 +170,37 @@
|
||||
padding: var(--gap-sm);
|
||||
}
|
||||
|
||||
&-log,
|
||||
&-inspect {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
// 火狐浏览器
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #6b6b6b #2b2b2b;
|
||||
&::-webkit-scrollbar {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
background-color: #2c2c2c;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-left: 1px solid #3d3d3d;
|
||||
// border-top: 1px solid #3d3d3d;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #6b6b6b;
|
||||
background-clip: content-box;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 500px;
|
||||
|
||||
&:hover {
|
||||
background: #939393;
|
||||
background-clip: content-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-logLine {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
@ -89,11 +89,20 @@
|
||||
}
|
||||
|
||||
&-body {
|
||||
padding: var(--drawer-content-paddingTop) var(--drawer-content-paddingRight)
|
||||
padding: 0 var(--drawer-content-paddingRight)
|
||||
var(--drawer-content-paddingBottom) var(--drawer-content-paddingLeft);
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
|
||||
// 因为如果成员里面有 position:sticky 的内容
|
||||
// 用 padding 会导致位置不正确
|
||||
// 所以改成这种写法
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
height: var(--drawer-content-paddingTop);
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
|
@ -350,6 +350,7 @@
|
||||
|
||||
&-submenu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.#{$ns}Nav-Menu-item-wrap {
|
||||
|
@ -433,7 +433,7 @@
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: transparent;
|
||||
background: var(--Table-onHover-bg);
|
||||
}
|
||||
|
||||
&:hover.#{$ns}Table-placeholder {
|
||||
|
@ -2,97 +2,108 @@
|
||||
title: Width
|
||||
---
|
||||
|
||||
| Class | Properties |
|
||||
| ----------- | ------------------------ |
|
||||
| w-px | width: 0.0625rem |
|
||||
| w-0 | width: 0 |
|
||||
| w-none | width: 0 |
|
||||
| w-0\.5 | width: 0.125rem |
|
||||
| w-1 | width: 0.25rem |
|
||||
| w-1\.5 | width: 0.375rem |
|
||||
| w-2 | width: 0.5rem |
|
||||
| w-2\.5 | width: 0.625rem |
|
||||
| w-3 | width: 0.75rem |
|
||||
| w-3\.5 | width: 0.875rem |
|
||||
| w-4 | width: 1rem |
|
||||
| w-5 | width: 1.25rem |
|
||||
| w-6 | width: 1.5rem |
|
||||
| w-7 | width: 1.75rem |
|
||||
| w-8 | width: 2rem |
|
||||
| w-9 | width: 2.25rem |
|
||||
| w-10 | width: 2.5rem |
|
||||
| w-11 | width: 2.75rem |
|
||||
| w-12 | width: 3rem |
|
||||
| w-14 | width: 3.5rem |
|
||||
| w-16 | width: 4rem |
|
||||
| w-18 | width: 4.5rem |
|
||||
| w-20 | width: 5rem |
|
||||
| w-24 | width: 6rem |
|
||||
| w-28 | width: 7rem |
|
||||
| w-32 | width: 8rem |
|
||||
| w-36 | width: 9rem |
|
||||
| w-40 | width: 10rem |
|
||||
| w-44 | width: 11rem |
|
||||
| w-48 | width: 12rem |
|
||||
| w-52 | width: 13rem |
|
||||
| w-56 | width: 14rem |
|
||||
| w-60 | width: 15rem |
|
||||
| w-64 | width: 16rem |
|
||||
| w-72 | width: 18rem |
|
||||
| w-80 | width: 20rem |
|
||||
| w-96 | width: 24rem |
|
||||
| w-auto | width: auto |
|
||||
| w-1x | width: 1em |
|
||||
| w-2x | width: 2em |
|
||||
| w-3x | width: 3em |
|
||||
| w-1\/2 | width: 50% |
|
||||
| w-1\/3 | width: 33.333333% |
|
||||
| w-2\/3 | width: 66.666667% |
|
||||
| w-1\/4 | width: 25% |
|
||||
| w-2\/4 | width: 50% |
|
||||
| w-3\/4 | width: 75% |
|
||||
| w-1\/5 | width: 20% |
|
||||
| w-2\/5 | width: 40% |
|
||||
| w-3\/5 | width: 60% |
|
||||
| w-4\/5 | width: 80% |
|
||||
| w-1\/6 | width: 16.666667% |
|
||||
| w-2\/6 | width: 33.333333% |
|
||||
| w-3\/6 | width: 50% |
|
||||
| w-4\/6 | width: 66.666667% |
|
||||
| w-5\/6 | width: 83.333333% |
|
||||
| w-1\/12 | width: 8.333333% |
|
||||
| w-2\/12 | width: 16.666667% |
|
||||
| w-3\/12 | width: 25% |
|
||||
| w-4\/12 | width: 33.333333% |
|
||||
| w-5\/12 | width: 41.666667% |
|
||||
| w-6\/12 | width: 50% |
|
||||
| w-7\/12 | width: 58.333333% |
|
||||
| w-8\/12 | width: 66.666667% |
|
||||
| w-9\/12 | width: 75% |
|
||||
| w-10\/12 | width: 83.333333% |
|
||||
| w-11\/12 | width: 91.666667% |
|
||||
| w-full | width: 100% |
|
||||
| w-screen | width: 100vw |
|
||||
| w-min | width: min-content |
|
||||
| w-max | width: max-content |
|
||||
| min-w-0 | min-width: 0px |
|
||||
| min-w-full | min-width: 100% |
|
||||
| min-w-min | min-width: min-content |
|
||||
| min-w-max | min-width: max-content |
|
||||
| max-w-none | max-width: none |
|
||||
| max-w-0 | max-width: 0rem |
|
||||
| max-w-xs | max-width: 20rem |
|
||||
| max-w-sm | max-width: 24rem |
|
||||
| max-w-md | max-width: 28rem |
|
||||
| max-w-lg | max-width: 32rem |
|
||||
| max-w-xl | max-width: 36rem |
|
||||
| max-w-2xl | max-width: 42rem |
|
||||
| max-w-3xl | max-width: 48rem |
|
||||
| max-w-4xl | max-width: 56rem |
|
||||
| max-w-5xl | max-width: 64rem |
|
||||
| max-w-6xl | max-width: 72rem |
|
||||
| max-w-7xl | max-width: 80rem |
|
||||
| max-w-full | max-width: 100% |
|
||||
| max-w-min | max-width: min-content |
|
||||
| max-w-max | max-width: max-content |
|
||||
| max-w-prose | max-width: 65ch |
|
||||
| Class | Properties |
|
||||
| ----------- | ---------------------- |
|
||||
| w-px | width: 0.0625rem |
|
||||
| w-0 | width: 0 |
|
||||
| w-none | width: 0 |
|
||||
| w-0\.5 | width: 0.125rem |
|
||||
| w-1 | width: 0.25rem |
|
||||
| w-1\.5 | width: 0.375rem |
|
||||
| w-2 | width: 0.5rem |
|
||||
| w-2\.5 | width: 0.625rem |
|
||||
| w-3 | width: 0.75rem |
|
||||
| w-3\.5 | width: 0.875rem |
|
||||
| w-4 | width: 1rem |
|
||||
| w-5 | width: 1.25rem |
|
||||
| w-6 | width: 1.5rem |
|
||||
| w-7 | width: 1.75rem |
|
||||
| w-8 | width: 2rem |
|
||||
| w-9 | width: 2.25rem |
|
||||
| w-10 | width: 2.5rem |
|
||||
| w-11 | width: 2.75rem |
|
||||
| w-12 | width: 3rem |
|
||||
| w-14 | width: 3.5rem |
|
||||
| w-16 | width: 4rem |
|
||||
| w-18 | width: 4.5rem |
|
||||
| w-20 | width: 5rem |
|
||||
| w-24 | width: 6rem |
|
||||
| w-28 | width: 7rem |
|
||||
| w-32 | width: 8rem |
|
||||
| w-36 | width: 9rem |
|
||||
| w-40 | width: 10rem |
|
||||
| w-44 | width: 11rem |
|
||||
| w-48 | width: 12rem |
|
||||
| w-52 | width: 13rem |
|
||||
| w-56 | width: 14rem |
|
||||
| w-60 | width: 15rem |
|
||||
| w-64 | width: 16rem |
|
||||
| w-72 | width: 18rem |
|
||||
| w-80 | width: 20rem |
|
||||
| w-96 | width: 24rem |
|
||||
| w-auto | width: auto |
|
||||
| w-1x | width: 1em |
|
||||
| w-2x | width: 2em |
|
||||
| w-3x | width: 3em |
|
||||
| w-1\/2 | width: 50% |
|
||||
| w-1\/3 | width: 33.333333% |
|
||||
| w-2\/3 | width: 66.666667% |
|
||||
| w-1\/4 | width: 25% |
|
||||
| w-2\/4 | width: 50% |
|
||||
| w-3\/4 | width: 75% |
|
||||
| w-1\/5 | width: 20% |
|
||||
| w-2\/5 | width: 40% |
|
||||
| w-3\/5 | width: 60% |
|
||||
| w-4\/5 | width: 80% |
|
||||
| w-1\/6 | width: 16.666667% |
|
||||
| w-2\/6 | width: 33.333333% |
|
||||
| w-3\/6 | width: 50% |
|
||||
| w-4\/6 | width: 66.666667% |
|
||||
| w-5\/6 | width: 83.333333% |
|
||||
| w-1\/12 | width: 8.333333% |
|
||||
| w-2\/12 | width: 16.666667% |
|
||||
| w-3\/12 | width: 25% |
|
||||
| w-4\/12 | width: 33.333333% |
|
||||
| w-5\/12 | width: 41.666667% |
|
||||
| w-6\/12 | width: 50% |
|
||||
| w-7\/12 | width: 58.333333% |
|
||||
| w-8\/12 | width: 66.666667% |
|
||||
| w-9\/12 | width: 75% |
|
||||
| w-10\/12 | width: 83.333333% |
|
||||
| w-11\/12 | width: 91.666667% |
|
||||
| w-full | width: 100% |
|
||||
| w-screen | width: 100vw |
|
||||
| min-w-none | min-width: none |
|
||||
| min-w-0 | min-width: 0rem |
|
||||
| min-w-xs | min-width: 20rem |
|
||||
| min-w-sm | min-width: 24rem |
|
||||
| min-w-md | min-width: 28rem |
|
||||
| min-w-lg | min-width: 32rem |
|
||||
| min-w-xl | min-width: 36rem |
|
||||
| min-w-2xl | min-width: 42rem |
|
||||
| min-w-3xl | min-width: 48rem |
|
||||
| min-w-4xl | min-width: 56rem |
|
||||
| min-w-5xl | min-width: 64rem |
|
||||
| min-w-6xl | min-width: 72rem |
|
||||
| min-w-7xl | min-width: 80rem |
|
||||
| min-w-full | min-width: 100% |
|
||||
| min-w-min | min-width: min-content |
|
||||
| min-w-max | min-width: max-content |
|
||||
| min-w-prose | min-width: 65ch |
|
||||
| max-w-none | max-width: none |
|
||||
| max-w-0 | max-width: 0rem |
|
||||
| max-w-xs | max-width: 20rem |
|
||||
| max-w-sm | max-width: 24rem |
|
||||
| max-w-md | max-width: 28rem |
|
||||
| max-w-lg | max-width: 32rem |
|
||||
| max-w-xl | max-width: 36rem |
|
||||
| max-w-2xl | max-width: 42rem |
|
||||
| max-w-3xl | max-width: 48rem |
|
||||
| max-w-4xl | max-width: 56rem |
|
||||
| max-w-5xl | max-width: 64rem |
|
||||
| max-w-6xl | max-width: 72rem |
|
||||
| max-w-7xl | max-width: 80rem |
|
||||
| max-w-full | max-width: 100% |
|
||||
| max-w-min | max-width: min-content |
|
||||
| max-w-max | max-width: max-content |
|
||||
| max-w-prose | max-width: 65ch |
|
||||
|
@ -115,6 +115,21 @@ title: Width
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@mixin min-width-sizing($map: $minWidths, $prefix: '.') {
|
||||
@each $name, $value in $map {
|
||||
@if $name == default {
|
||||
#{$prefix}min-w {
|
||||
min-width: $value;
|
||||
}
|
||||
} @else {
|
||||
#{$prefix}min-w-#{'' + selector-escape($name)} {
|
||||
min-width: $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin max-width-sizing($map: $maxWidths, $prefix: '.') {
|
||||
@each $name, $value in $map {
|
||||
@if $name == default {
|
||||
@ -148,6 +163,7 @@ title: Width
|
||||
}
|
||||
|
||||
@include max-width-sizing($maxWidths, $prefix);
|
||||
@include min-width-sizing($minWidths, $prefix);
|
||||
}
|
||||
|
||||
@include make-widths();
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @author fex
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {version} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import Modal from './Modal';
|
||||
import Button from './Button';
|
||||
@ -11,6 +11,7 @@ import {ClassNamesFn, themeable, ThemeProps} from 'amis-core';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import Html from './Html';
|
||||
import type {PlainObject} from 'amis-core';
|
||||
// import {createRoot} from 'react-dom/client';
|
||||
export interface AlertProps extends ThemeProps, LocaleProps {
|
||||
container?: any;
|
||||
confirmText?: string;
|
||||
@ -52,13 +53,21 @@ export interface AlertState {
|
||||
|
||||
export class Alert extends React.Component<AlertProps, AlertState> {
|
||||
static instance: any = null;
|
||||
static getInstance() {
|
||||
static async getInstance() {
|
||||
if (!Alert.instance) {
|
||||
console.warn('Alert 组件应该没有被渲染,所以隐性的渲染到 body 了');
|
||||
const container = document.body;
|
||||
const div = document.createElement('div');
|
||||
container.appendChild(div);
|
||||
|
||||
// if (parseInt(version.split('.')[0], 10) >= 18) {
|
||||
// const root = createRoot(div);
|
||||
// await new Promise<void>(resolve =>
|
||||
// root.render(<FinnalAlert ref={() => resolve()} />)
|
||||
// );
|
||||
// } else {
|
||||
render(<FinnalAlert />, div);
|
||||
// }
|
||||
}
|
||||
|
||||
return Alert.instance;
|
||||
@ -346,23 +355,35 @@ function renderForm(
|
||||
return renderSchemaFn?.(controls, value, callback, scopeRef, theme);
|
||||
}
|
||||
|
||||
export const alert: (content: string, title?: string) => void = (
|
||||
export const alert: (content: string, title?: string) => Promise<void> = async (
|
||||
content,
|
||||
title
|
||||
) => Alert.getInstance().alert(content, title);
|
||||
) => {
|
||||
const instance = await Alert.getInstance();
|
||||
return instance.alert(content, title);
|
||||
};
|
||||
export const confirm: (
|
||||
content: string | React.ReactNode,
|
||||
title?: string,
|
||||
optionsOrCofnrimText?: string | ConfirmOptions,
|
||||
cancelText?: string
|
||||
) => Promise<any> = (content, title, optionsOrCofnrimText, cancelText) =>
|
||||
Alert.getInstance().confirm(content, title, optionsOrCofnrimText, cancelText);
|
||||
) => Promise<any> = async (
|
||||
content,
|
||||
title,
|
||||
optionsOrCofnrimText,
|
||||
cancelText
|
||||
) => {
|
||||
const instance = await Alert.getInstance();
|
||||
return instance.confirm(content, title, optionsOrCofnrimText, cancelText);
|
||||
};
|
||||
export const prompt: (
|
||||
controls: any,
|
||||
defaultvalue?: any,
|
||||
title?: string,
|
||||
confirmText?: string
|
||||
) => Promise<any> = (controls, defaultvalue, title, confirmText) =>
|
||||
Alert.getInstance().prompt(controls, defaultvalue, title, confirmText);
|
||||
) => Promise<any> = async (controls, defaultvalue, title, confirmText) => {
|
||||
const instance = await Alert.getInstance();
|
||||
return instance.prompt(controls, defaultvalue, title, confirmText);
|
||||
};
|
||||
export const FinnalAlert = themeable(localeable(Alert));
|
||||
export default FinnalAlert;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {ClassNamesFn, themeable} from 'amis-core';
|
||||
import React from 'react';
|
||||
import React, {version} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import {autobind, calculatePosition} from 'amis-core';
|
||||
import Transition, {
|
||||
@ -7,6 +7,7 @@ import Transition, {
|
||||
ENTERING,
|
||||
EXITING
|
||||
} from 'react-transition-group/Transition';
|
||||
// import {createRoot} from 'react-dom/client';
|
||||
const fadeStyles: {
|
||||
[propName: string]: string;
|
||||
} = {
|
||||
@ -49,12 +50,20 @@ export class ContextMenu extends React.Component<
|
||||
ContextMenuState
|
||||
> {
|
||||
static instance: any = null;
|
||||
static getInstance() {
|
||||
static async getInstance() {
|
||||
if (!ContextMenu.instance) {
|
||||
const container = document.body;
|
||||
const div = document.createElement('div');
|
||||
container.appendChild(div);
|
||||
|
||||
// if (parseInt(version.split('.')[0], 10) >= 18) {
|
||||
// const root = createRoot(div);
|
||||
// await new Promise<void>(resolve =>
|
||||
// root.render(<ThemedContextMenu ref={() => resolve()} />)
|
||||
// );
|
||||
// } else {
|
||||
render(<ThemedContextMenu />, div);
|
||||
// }
|
||||
}
|
||||
|
||||
return ContextMenu.instance;
|
||||
@ -309,5 +318,7 @@ export function openContextMenus(
|
||||
menus: Array<MenuItem | MenuDivider>,
|
||||
onClose?: () => void
|
||||
) {
|
||||
return ContextMenu.getInstance().openContextMenus(info, menus, onClose);
|
||||
return ContextMenu.getInstance().then(instance =>
|
||||
instance.openContextMenus(info, menus, onClose)
|
||||
);
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ export class CBGroupOrItem extends React.Component<CBGroupOrItemProps> {
|
||||
onDragStart={onDragStart}
|
||||
config={config}
|
||||
fields={fields}
|
||||
formula={formula}
|
||||
value={value as ConditionGroupValue}
|
||||
onChange={this.handleItemChange}
|
||||
fieldClassName={fieldClassName}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, {useEffect} from 'react';
|
||||
|
||||
import {themeable, ThemeProps, filterTree, getTreeAncestors} from 'amis-core';
|
||||
import {themeable, ThemeProps, filterTree} from 'amis-core';
|
||||
import GroupedSelection from '../GroupedSelection';
|
||||
import Tabs, {Tab} from '../Tabs';
|
||||
import TreeSelection from '../TreeSelection';
|
||||
@ -54,7 +54,7 @@ const memberOpers = [
|
||||
{
|
||||
label: '取该成员的平均值',
|
||||
value: 'AVG(ARRAYMAP(${arr}, item => item.${member}))',
|
||||
description: '即计算该成员记录的总和,需确认该成员记录均为数字类型'
|
||||
description: '即计算该成员记录的平均值,需确认该成员记录均为数字类型'
|
||||
},
|
||||
{
|
||||
label: '取该成员的最大值',
|
||||
@ -140,48 +140,51 @@ function VariableList(props: VariableListProps) {
|
||||
)}
|
||||
{/* 控制只对第一层数组成员展示快捷操作入口 */}
|
||||
{option.memberDepth !== undefined &&
|
||||
option.memberDepth < 2 &&
|
||||
option.label &&
|
||||
(!selfVariableName || option.value !== selfVariableName) ? (
|
||||
<PopOverContainer
|
||||
popOverContainer={() =>
|
||||
document.querySelector(`.${cx('FormulaPicker-Modal')}`)
|
||||
}
|
||||
popOverRender={({onClose}) => (
|
||||
<ul className={cx(`${classPrefix}-item-oper`)}>
|
||||
{memberOpers.map((item, i) => {
|
||||
return (
|
||||
<TooltipWrapper
|
||||
tooltip={item.description}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<li
|
||||
key={i}
|
||||
onClick={() =>
|
||||
handleMemberClick(
|
||||
{...item, isMember: true},
|
||||
option,
|
||||
onClose
|
||||
)
|
||||
}
|
||||
option.memberDepth < 2 ? (
|
||||
<PopOverContainer
|
||||
popOverContainer={() =>
|
||||
document.querySelector(`.${cx('FormulaPicker-Modal')}`)
|
||||
}
|
||||
popOverRender={({onClose}) => (
|
||||
<ul className={cx(`${classPrefix}-item-oper`)}>
|
||||
{memberOpers.map((item, i) => {
|
||||
return (
|
||||
<TooltipWrapper
|
||||
tooltip={item.description}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
</li>
|
||||
</TooltipWrapper>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
>
|
||||
{({onClick, ref, isOpened}) => (
|
||||
<TooltipWrapper
|
||||
tooltip={option.description ?? option.label}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<label onClick={onClick}>{option.label}</label>
|
||||
</TooltipWrapper>
|
||||
)}
|
||||
</PopOverContainer>
|
||||
<li
|
||||
key={i}
|
||||
onClick={() =>
|
||||
handleMemberClick(
|
||||
{...item, isMember: true},
|
||||
option,
|
||||
onClose
|
||||
)
|
||||
}
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
</li>
|
||||
</TooltipWrapper>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
>
|
||||
{({onClick, ref, isOpened}) => (
|
||||
<TooltipWrapper
|
||||
tooltip={option.description ?? option.label}
|
||||
tooltipTheme="dark"
|
||||
>
|
||||
<label onClick={onClick}>{option.label}</label>
|
||||
</TooltipWrapper>
|
||||
)}
|
||||
</PopOverContainer>
|
||||
) : (
|
||||
<label>{option.label}</label>
|
||||
)
|
||||
) : null}
|
||||
{option?.tag ? (
|
||||
<span className={cx(`${classPrefix}-item-tag`)}>
|
||||
|
@ -1,58 +1,58 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EventAction:ajax 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--primary cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
发送请求
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
<span>
|
||||
18岁的天空
|
||||
</span>
|
||||
</span>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--primary cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
发送请求2
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
<span>
|
||||
岁的天空,status:,msg:
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:ajax 2`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--primary cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
发送请求
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
<span>
|
||||
18岁的天空
|
||||
</span>
|
||||
</span>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--primary cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
发送请求2
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
<span>
|
||||
18岁的天空,status:0,msg:ok
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:ajax args 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
|
@ -297,3 +297,301 @@ exports[`EventAction:dialog 7`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="amis-dialog-widget cxd-Modal cxd-Modal--1th"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="cxd-Modal-overlay in"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Modal-content in"
|
||||
>
|
||||
<div
|
||||
class="cxd-Modal-header"
|
||||
>
|
||||
<a
|
||||
class="cxd-Modal-close"
|
||||
data-position="left"
|
||||
data-tooltip="关闭"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-close"
|
||||
icon="close"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="cxd-Modal-title"
|
||||
>
|
||||
模态弹窗
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Modal-body"
|
||||
role="dialog-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开子弹窗
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default ml-2"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
关闭当前弹窗
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default ml-2"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
触发确认
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default ml-2"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
触发取消
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Modal-footer"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
取消
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--primary cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
确认
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 2`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 3`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 4`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 5`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 6`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:dialog args 7`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开弹窗
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -279,3 +279,283 @@ exports[`EventAction:drawer 7`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="amis-dialog-widget cxd-Drawer cxd-Drawer--right cxd-Drawer--md cxd-Modal--1th"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="cxd-Drawer-overlay in"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Drawer-content in"
|
||||
>
|
||||
<a
|
||||
class="cxd-Drawer-close"
|
||||
>
|
||||
<icon-mock
|
||||
classname="icon icon-close"
|
||||
icon="close"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="cxd-Drawer-header"
|
||||
>
|
||||
<div
|
||||
class="cxd-Drawer-title"
|
||||
>
|
||||
<span
|
||||
class="cxd-TplField"
|
||||
>
|
||||
<span>
|
||||
模态抽屉
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Drawer-body"
|
||||
>
|
||||
<div
|
||||
class="cxd-Spinner-overlay in"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Spinner cxd-Spinner--overlay in"
|
||||
data-testid="spinner"
|
||||
>
|
||||
<div
|
||||
class="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Drawer-footer"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
取消
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--primary cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
确认
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 2`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 3`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 4`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 5`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 6`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`EventAction:drawer args 7`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
打开抽屉
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,47 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EventAction:hidden 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
按钮2
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
按钮4
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
按钮5
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -3,7 +3,7 @@ import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
|
||||
test('EventAction:ajax', async () => {
|
||||
test('EventAction:ajax args', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
@ -108,7 +108,6 @@ test('EventAction:ajax', async () => {
|
||||
await waitFor(() => {
|
||||
expect(getByText('18岁的天空')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('发送请求2'));
|
||||
await waitFor(() => {
|
||||
@ -116,3 +115,310 @@ test('EventAction:ajax', async () => {
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('EventAction:ajax', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
age: 18
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
id: 'page_001',
|
||||
data: {
|
||||
name: 'lll'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '发送请求',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
url: '/api/xxx',
|
||||
method: 'get'
|
||||
},
|
||||
messages: {
|
||||
success: '成功了!欧耶',
|
||||
failed: '失败了呢。。'
|
||||
},
|
||||
outputVar: 'result'
|
||||
},
|
||||
{
|
||||
actionType: 'setValue',
|
||||
componentId: 'page_001',
|
||||
args: {
|
||||
value: '${event.data.result.responseData}'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${age}岁的天空'
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '发送请求2',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
url: '/api/xxx',
|
||||
method: 'get'
|
||||
},
|
||||
messages: {
|
||||
success: '成功了!欧耶',
|
||||
failed: '失败了呢。。'
|
||||
}
|
||||
},
|
||||
{
|
||||
actionType: 'setValue',
|
||||
componentId: 'page_001',
|
||||
args: {
|
||||
value: '${event.data}'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${responseResult.responseData.age}岁的天空,status:${responseResult.responseStatus},msg:${responseResult.responseMsg}'
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('发送请求'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('18岁的天空')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('发送请求2'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('18岁的天空,status:0,msg:ok')).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('EventAction:ajax data1', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
name: 'amis',
|
||||
age: 18
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
name: 'lll'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '发送请求',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'ajax',
|
||||
args: {
|
||||
api: {
|
||||
url: '/api/xxx?name=${event.data.name}',
|
||||
method: 'post',
|
||||
data: {
|
||||
myname1: '${name}',
|
||||
myname2: '\\${name}',
|
||||
myname3: '${text}',
|
||||
myname4: '\\${text}'
|
||||
}
|
||||
}
|
||||
},
|
||||
outputVar: 'result'
|
||||
},
|
||||
{
|
||||
actionType: 'ajax',
|
||||
args: {
|
||||
api: {
|
||||
url: '/api/xxx?q1=${result.responseData.age}',
|
||||
method: 'post',
|
||||
data: {
|
||||
param1: '${event.data.result.responseData.name}',
|
||||
param2: '${responseData.name}',
|
||||
param3: '${result.name}',
|
||||
param4: '${event.data.responseData.name}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: {
|
||||
text: '${lll}'
|
||||
}
|
||||
},
|
||||
makeEnv({
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('发送请求')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(getByText(/发送请求/));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetcher).toHaveBeenCalledTimes(2);
|
||||
expect(fetcher.mock.calls[0][0].url).toEqual('/api/xxx?name=lll');
|
||||
expect(fetcher.mock.calls[0][0].data).toMatchObject({
|
||||
myname1: 'lll',
|
||||
myname2: '${name}',
|
||||
myname3: '${lll}',
|
||||
myname4: '${text}'
|
||||
});
|
||||
expect(fetcher.mock.calls[1][0].url).toEqual('/api/xxx?q1=18');
|
||||
expect(fetcher.mock.calls[1][0].data).toMatchObject({
|
||||
param1: 'amis',
|
||||
param2: 'amis',
|
||||
param3: 'amis',
|
||||
param4: 'amis'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('EventAction:ajax data2', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
name: 'amis',
|
||||
age: 18
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
name: 'lll'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '发送请求',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
url: '/api/xxx?name=${event.data.name}',
|
||||
method: 'post',
|
||||
data: {
|
||||
myname1: '${name}',
|
||||
myname2: '\\${name}',
|
||||
myname3: '${text}',
|
||||
myname4: '\\${text}'
|
||||
}
|
||||
},
|
||||
outputVar: 'result'
|
||||
},
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
url: '/api/xxx?q1=${result.responseData.age}',
|
||||
method: 'post',
|
||||
data: {
|
||||
param1: '${event.data.result.responseData.name}',
|
||||
param2: '${responseData.name}',
|
||||
param3: '${result.name}',
|
||||
param4: '${event.data.responseData.name}'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: {
|
||||
text: '${lll}'
|
||||
}
|
||||
},
|
||||
makeEnv({
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('发送请求')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(getByText(/发送请求/));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetcher).toHaveBeenCalledTimes(2);
|
||||
expect(fetcher.mock.calls[0][0].url).toEqual('/api/xxx?name=lll');
|
||||
expect(fetcher.mock.calls[0][0].data).toMatchObject({
|
||||
myname1: 'lll',
|
||||
myname2: '${name}',
|
||||
myname3: '${lll}',
|
||||
myname4: '${text}'
|
||||
});
|
||||
expect(fetcher.mock.calls[1][0].url).toEqual('/api/xxx?q1=18');
|
||||
expect(fetcher.mock.calls[1][0].data).toMatchObject({
|
||||
param1: 'amis',
|
||||
param2: 'amis',
|
||||
param3: 'amis',
|
||||
param4: 'amis'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -35,7 +35,7 @@ test('EventAction:custom', async () => {
|
||||
actionType: 'custom',
|
||||
args: {
|
||||
script:
|
||||
"doAction({actionType: 'ajax', args: {api: '/api/xxx'}, outputVar: 'result'});"
|
||||
"doAction({actionType: 'ajax', api: '/api/xxx', outputVar: 'result'});"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -60,7 +60,7 @@ test('EventAction:custom', async () => {
|
||||
actionType: 'custom',
|
||||
args: {
|
||||
script:
|
||||
"doAction({actionType: 'ajax', args: {api: '/api/xxx'}, outputVar: 'result'});event.stopPropagation();"
|
||||
"doAction({actionType: 'ajax', api: '/api/xxx', outputVar: 'result'});event.stopPropagation();"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3,7 +3,7 @@ import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
|
||||
test('EventAction:dialog', async () => {
|
||||
test('EventAction:dialog args', async () => {
|
||||
const notify = jest.fn();
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
@ -220,97 +220,422 @@ test('EventAction:dialog', async () => {
|
||||
expect(container).toMatchSnapshot();
|
||||
}, 7000);
|
||||
|
||||
// test('EventAction:alert', async () => {
|
||||
// const alert = jest.fn();
|
||||
// const {getByText, container}: any = render(
|
||||
// amisRender(
|
||||
// {
|
||||
// type: 'page',
|
||||
// data: {
|
||||
// msg: '去吃饭了'
|
||||
// },
|
||||
// body: [
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '提示对话框',
|
||||
// level: 'primary',
|
||||
// onEvent: {
|
||||
// click: {
|
||||
// actions: [
|
||||
// {
|
||||
// actionType: 'alert',
|
||||
// args: {
|
||||
// msg: '<a href="http://www.baidu.com" target="_blank">${msg}~</a>'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {},
|
||||
// makeEnv({
|
||||
// getModalContainer: () => container,
|
||||
// alert
|
||||
// })
|
||||
// )
|
||||
// );
|
||||
test('EventAction:dialog', async () => {
|
||||
const notify = jest.fn();
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开弹窗',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
id: 'dialog_001',
|
||||
title: '模态弹窗',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开子弹窗',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
title: '模态子弹窗',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '关闭父弹窗',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'closeDialog',
|
||||
componentId: 'dialog_001'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '关闭当前弹窗',
|
||||
className: 'ml-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'closeDialog'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '触发确认',
|
||||
className: 'ml-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'confirm',
|
||||
componentId: 'dialog_001'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '触发取消',
|
||||
className: 'ml-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'cancel',
|
||||
componentId: 'dialog_001'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
onEvent: {
|
||||
confirm: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msg: 'confirm'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
cancel: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msg: 'cancel'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container,
|
||||
notify
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// fireEvent.click(getByText('提示对话框'));
|
||||
// await waitFor(() => {
|
||||
// expect(alert).toHaveBeenCalled();
|
||||
// });
|
||||
// expect(alert.mock.calls[0][0]).toEqual(
|
||||
// '<a href="http://www.baidu.com" target="_blank">去吃饭了~</a>'
|
||||
// );
|
||||
// });
|
||||
// events
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
// test('EventAction:confirm', async () => {
|
||||
// const confirm = jest.fn();
|
||||
// const {getByText, container}: any = render(
|
||||
// amisRender(
|
||||
// {
|
||||
// type: 'page',
|
||||
// data: {
|
||||
// title: '操作确认',
|
||||
// msg: '要删除它吗?'
|
||||
// },
|
||||
// body: [
|
||||
// {
|
||||
// type: 'button',
|
||||
// label: '确认对话框',
|
||||
// level: 'primary',
|
||||
// onEvent: {
|
||||
// click: {
|
||||
// actions: [
|
||||
// {
|
||||
// actionType: 'confirmDialog',
|
||||
// args: {
|
||||
// title: '${title}',
|
||||
// msg: '<span style="color:red">${msg}</span>'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {},
|
||||
// makeEnv({
|
||||
// getModalContainer: () => container,
|
||||
// confirm
|
||||
// })
|
||||
// )
|
||||
// );
|
||||
await waitFor(() => {
|
||||
expect(getByText('确认')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('确认'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
// fireEvent.click(getByText('确认对话框'));
|
||||
// await waitFor(() => {
|
||||
// expect(confirm).toHaveBeenCalled();
|
||||
// });
|
||||
// expect(confirm.mock.calls[0][0]).toEqual(
|
||||
// '<span style="color:red">要删除它吗?</span>'
|
||||
// );
|
||||
// expect(confirm.mock.calls[0][1]).toEqual('操作确认');
|
||||
// });
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('取消')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('取消'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
// actions
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('关闭当前弹窗')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('关闭当前弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('打开子弹窗')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('打开子弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('关闭父弹窗')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('关闭父弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('触发确认')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('触发确认'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('触发取消')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('触发取消'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
}, 7000);
|
||||
|
||||
test('EventAction:dialog data', async () => {
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
name: 'amis'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开弹窗',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
args: {
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
id: 'dialog_001',
|
||||
title: '模态弹窗${event.data.name}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '你好,我是${name}、${name1}、${name2}'
|
||||
}
|
||||
],
|
||||
data: {
|
||||
name1: '${name}',
|
||||
name2: '${event.data.name}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// events
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).toBeInTheDocument();
|
||||
expect(getByText('模态弹窗')).toBeInTheDocument();
|
||||
expect(getByText('你好,我是、amis、amis')).toBeInTheDocument(); // 因为事件动作给args提前做了映射
|
||||
});
|
||||
}, 7000);
|
||||
|
||||
test('EventAction:dialog data2', async () => {
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
name: 'amis'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开弹窗',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
type: 'dialog',
|
||||
id: 'dialog_001',
|
||||
title: '模态弹窗${event.data.name}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '你好,我是${name}、${name1}、${name2}'
|
||||
}
|
||||
],
|
||||
data: {
|
||||
name1: '${name}',
|
||||
name2: '${event.data.name}'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// events
|
||||
fireEvent.click(getByText('打开弹窗'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).toBeInTheDocument();
|
||||
expect(getByText('模态弹窗')).toBeInTheDocument();
|
||||
expect(getByText('你好,我是、amis、amis')).toBeInTheDocument();
|
||||
});
|
||||
}, 7000);
|
||||
|
||||
// // test('EventAction:alert', async () => {
|
||||
// // const alert = jest.fn();
|
||||
// // const {getByText, container}: any = render(
|
||||
// // amisRender(
|
||||
// // {
|
||||
// // type: 'page',
|
||||
// // data: {
|
||||
// // msg: '去吃饭了'
|
||||
// // },
|
||||
// // body: [
|
||||
// // {
|
||||
// // type: 'button',
|
||||
// // label: '提示对话框',
|
||||
// // level: 'primary',
|
||||
// // onEvent: {
|
||||
// // click: {
|
||||
// // actions: [
|
||||
// // {
|
||||
// // actionType: 'alert',
|
||||
// // args: {
|
||||
// // msg: '<a href="http://www.baidu.com" target="_blank">${msg}~</a>'
|
||||
// // }
|
||||
// // }
|
||||
// // ]
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // ]
|
||||
// // },
|
||||
// // {},
|
||||
// // makeEnv({
|
||||
// // getModalContainer: () => container,
|
||||
// // alert
|
||||
// // })
|
||||
// // )
|
||||
// // );
|
||||
|
||||
// // fireEvent.click(getByText('提示对话框'));
|
||||
// // await waitFor(() => {
|
||||
// // expect(alert).toHaveBeenCalled();
|
||||
// // });
|
||||
// // expect(alert.mock.calls[0][0]).toEqual(
|
||||
// // '<a href="http://www.baidu.com" target="_blank">去吃饭了~</a>'
|
||||
// // );
|
||||
// // });
|
||||
|
||||
// // test('EventAction:confirm', async () => {
|
||||
// // const confirm = jest.fn();
|
||||
// // const {getByText, container}: any = render(
|
||||
// // amisRender(
|
||||
// // {
|
||||
// // type: 'page',
|
||||
// // data: {
|
||||
// // title: '操作确认',
|
||||
// // msg: '要删除它吗?'
|
||||
// // },
|
||||
// // body: [
|
||||
// // {
|
||||
// // type: 'button',
|
||||
// // label: '确认对话框',
|
||||
// // level: 'primary',
|
||||
// // onEvent: {
|
||||
// // click: {
|
||||
// // actions: [
|
||||
// // {
|
||||
// // actionType: 'confirmDialog',
|
||||
// // args: {
|
||||
// // title: '${title}',
|
||||
// // msg: '<span style="color:red">${msg}</span>'
|
||||
// // }
|
||||
// // }
|
||||
// // ]
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // ]
|
||||
// // },
|
||||
// // {},
|
||||
// // makeEnv({
|
||||
// // getModalContainer: () => container,
|
||||
// // confirm
|
||||
// // })
|
||||
// // )
|
||||
// // );
|
||||
|
||||
// // fireEvent.click(getByText('确认对话框'));
|
||||
// // await waitFor(() => {
|
||||
// // expect(confirm).toHaveBeenCalled();
|
||||
// // });
|
||||
// // expect(confirm.mock.calls[0][0]).toEqual(
|
||||
// // '<span style="color:red">要删除它吗?</span>'
|
||||
// // );
|
||||
// // expect(confirm.mock.calls[0][1]).toEqual('操作确认');
|
||||
// // });
|
||||
|
@ -3,6 +3,223 @@ import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
|
||||
test('EventAction:drawer args', async () => {
|
||||
const notify = jest.fn();
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开抽屉',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'drawer',
|
||||
args: {
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
id: 'drawer_001',
|
||||
title: '模态抽屉',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开子抽屉',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'drawer',
|
||||
args: {
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
title: '模态子抽屉',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '关闭父抽屉',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'closeDrawer',
|
||||
componentId: 'drawer_001'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '关闭当前抽屉',
|
||||
className: 'ml-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'closeDrawer'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '触发确认',
|
||||
className: 'ml-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'confirm',
|
||||
componentId: 'drawer_001'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '触发取消',
|
||||
className: 'ml-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'cancel',
|
||||
componentId: 'drawer_001'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
onEvent: {
|
||||
confirm: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msg: 'confirm'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
cancel: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msg: 'cancel'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container,
|
||||
notify
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// events
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('确认')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('确认'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('取消')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('取消'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
// actions
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('关闭当前抽屉')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('关闭当前抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('打开子抽屉')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('打开子抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('关闭父抽屉')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('关闭父抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('触发确认')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('触发确认'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(getByText('触发取消')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(getByText('触发取消'));
|
||||
await wait(300);
|
||||
expect(notify).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
}, 7000);
|
||||
|
||||
test('EventAction:drawer', async () => {
|
||||
const notify = jest.fn();
|
||||
const {getByText, container}: any = render(
|
||||
@ -215,3 +432,115 @@ test('EventAction:drawer', async () => {
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
}, 7000);
|
||||
|
||||
test('EventAction:drawer data', async () => {
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
name: 'amis'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开抽屉',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'drawer',
|
||||
args: {
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
id: 'drawer_001',
|
||||
title: '模态弹窗${event.data.name}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '你好,我是${name}、${name1}、${name2}'
|
||||
}
|
||||
],
|
||||
data: {
|
||||
name1: '${name}',
|
||||
name2: '${event.data.name}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// events
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).toBeInTheDocument();
|
||||
expect(getByText('模态弹窗')).toBeInTheDocument();
|
||||
expect(getByText('你好,我是、amis、amis')).toBeInTheDocument(); // 因为事件动作给args提前做了映射
|
||||
});
|
||||
}, 7000);
|
||||
|
||||
test('EventAction:drawer data2', async () => {
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
data: {
|
||||
name: 'amis'
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '打开抽屉',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'drawer',
|
||||
drawer: {
|
||||
type: 'drawer',
|
||||
id: 'drawer_001',
|
||||
title: '模态弹窗${event.data.name}',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '你好,我是${name}、${name1}、${name2}'
|
||||
}
|
||||
],
|
||||
data: {
|
||||
name1: '${name}',
|
||||
name2: '${event.data.name}'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// events
|
||||
fireEvent.click(getByText('打开抽屉'));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('[role="dialog"]')).toBeInTheDocument();
|
||||
expect(getByText('模态弹窗')).toBeInTheDocument();
|
||||
expect(getByText('你好,我是、amis、amis')).toBeInTheDocument();
|
||||
});
|
||||
}, 7000);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {fireEvent, render} from '@testing-library/react';
|
||||
import {fireEvent, render, waitFor} from '@testing-library/react';
|
||||
import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv} from '../helper';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
|
||||
test('EventAction:hidden', async () => {
|
||||
const {getByText, container}: any = render(
|
||||
@ -21,6 +21,7 @@ test('EventAction:hidden', async () => {
|
||||
{
|
||||
type: 'action',
|
||||
label: '按钮2',
|
||||
className: 'btn_2',
|
||||
hiddenOn: '${btnNotHidden}',
|
||||
onEvent: {
|
||||
click: {
|
||||
@ -36,12 +37,14 @@ test('EventAction:hidden', async () => {
|
||||
{
|
||||
type: 'action',
|
||||
label: '按钮3',
|
||||
className: 'btn_3',
|
||||
hiddenOn: '${btnNotHidden}',
|
||||
id: 'ui:button_test_3'
|
||||
},
|
||||
{
|
||||
type: 'action',
|
||||
label: '按钮4',
|
||||
className: 'btn_4',
|
||||
hiddenOn: '${btnNotHidden}',
|
||||
onEvent: {
|
||||
click: {
|
||||
@ -57,6 +60,7 @@ test('EventAction:hidden', async () => {
|
||||
{
|
||||
type: 'action',
|
||||
label: '按钮5',
|
||||
className: 'btn_5',
|
||||
hidden: true,
|
||||
id: 'ui:button_test_5'
|
||||
}
|
||||
@ -67,8 +71,18 @@ test('EventAction:hidden', async () => {
|
||||
)
|
||||
);
|
||||
|
||||
fireEvent.click(getByText(/按钮2/));
|
||||
fireEvent.click(getByText(/按钮4/));
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('.btn_2')).toBeInTheDocument();
|
||||
expect(container.querySelector('.btn_3')).toBeInTheDocument();
|
||||
expect(container.querySelector('.btn_4')).toBeInTheDocument();
|
||||
expect(container.querySelector('.btn_5')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
fireEvent.click(getByText(/按钮2/));
|
||||
await wait(300);
|
||||
expect(container.querySelector('.btn_3')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(getByText(/按钮4/));
|
||||
await wait(300);
|
||||
expect(container.querySelector('.btn_5')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -75,7 +75,7 @@ exports[`doAction:service reload 1`] = `
|
||||
placeholder=""
|
||||
size="10"
|
||||
type="text"
|
||||
value="Amis Renderer"
|
||||
value="amis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -321,7 +321,7 @@ exports[`doAction:service reload 2`] = `
|
||||
placeholder=""
|
||||
size="10"
|
||||
type="text"
|
||||
value="Amis Renderer"
|
||||
value="amis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -427,7 +427,7 @@ test('doAction:form reload default', async () => {
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200); // 等待 initApi 加载完
|
||||
await wait(500); // 等待 initApi 加载完
|
||||
expect(
|
||||
(container.querySelector('[name="author"]') as HTMLInputElement).value
|
||||
).toEqual('fex');
|
||||
@ -527,7 +527,7 @@ test('doAction:form reload with data', async () => {
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
await wait(500);
|
||||
const author: HTMLInputElement = container.querySelector('[name="author"]')!;
|
||||
expect(author).toBeInTheDocument();
|
||||
fireEvent.change(author, {
|
||||
@ -698,7 +698,7 @@ test('doAction:form clear', async () => {
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
await wait(500);
|
||||
await waitFor(() => {
|
||||
expect(getByText('清空表单')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ test('EventAction:inputRange', async () => {
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
await wait(500);
|
||||
const inputs = container.querySelector('.cxd-InputRange-input input')!;
|
||||
|
||||
// input change
|
||||
|
@ -342,7 +342,7 @@ test('Renderers:Action tooltip', async () => {
|
||||
// });
|
||||
|
||||
// 14. confirmText
|
||||
test('Renderers:Action with confirmText & actionType ajax', () => {
|
||||
test('Renderers:Action with confirmText & actionType ajax', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
@ -372,7 +372,7 @@ test('Renderers:Action with confirmText & actionType ajax', () => {
|
||||
)
|
||||
);
|
||||
fireEvent.click(container.querySelector('.cxd-Button'));
|
||||
wait(500);
|
||||
await wait(500);
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
|
||||
expect(baseElement.querySelector('.cxd-Modal-content')!).toHaveTextContent(
|
||||
@ -380,14 +380,16 @@ test('Renderers:Action with confirmText & actionType ajax', () => {
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('取消'));
|
||||
wait(500);
|
||||
await wait(500);
|
||||
expect(fetcher).not.toBeCalled();
|
||||
|
||||
// fireEvent.click(container.querySelector('.cxd-Button'));
|
||||
// wait(500);
|
||||
// fireEvent.click(getByText('确认'));
|
||||
// fetcher 不生效
|
||||
// expect(fetcher).toBeCalled();
|
||||
fireEvent.click(container.querySelector('.cxd-Button'));
|
||||
await wait(500);
|
||||
fireEvent.click(getByText('确认'));
|
||||
|
||||
await wait(200);
|
||||
// fetcher 该被执行了
|
||||
expect(fetcher).toBeCalled();
|
||||
});
|
||||
|
||||
// 15.Action 作为容器组件
|
||||
|
@ -2,7 +2,7 @@ import React = require('react');
|
||||
import {render, fireEvent} from '@testing-library/react';
|
||||
import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv} from '../helper';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
import moment from 'moment';
|
||||
import {act} from 'react-test-renderer';
|
||||
|
||||
@ -89,3 +89,44 @@ test('Renderer:date reset', async () => {
|
||||
// 重制后的日期 等于初始化的日期
|
||||
expect(inputElement?.value === defaultValue).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Renderer:date defaultValue', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
data: {updateTime: 1680255708}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'form',
|
||||
initApi: {
|
||||
url: '/amis/initData',
|
||||
method: 'GET'
|
||||
},
|
||||
title: '编辑',
|
||||
body: [
|
||||
{
|
||||
type: 'input-date',
|
||||
label: '日期',
|
||||
name: 'updateTime',
|
||||
format: 'YYYY-MM-DD',
|
||||
value: '${NOW()}'
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
await wait(500);
|
||||
const inputElement = container.querySelector('input[type="text"]') as any;
|
||||
expect(inputElement?.value).toBe('2023-03-31'); // 默认值的优先级没有接口返回的高,所以应该是 2023-03-31
|
||||
});
|
||||
|
@ -391,7 +391,7 @@ test('Form:options:autoFill:validation', async () => {
|
||||
expect(option1.getAttribute('value')).toEqual('OptionB');
|
||||
expect(option1.getAttribute('value')).toEqual('OptionB');
|
||||
expect(screen.queryByText(validationMsg1)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(validationMsg2)).toBeInTheDocument();
|
||||
expect(screen.queryByText(validationMsg2)).not.toBeInTheDocument();
|
||||
|
||||
// 提交后校验信息全部消除
|
||||
fireEvent.click(submitBtn);
|
||||
|
@ -4,6 +4,7 @@ import '../../../src';
|
||||
import {render as amisRender} from '../../../src';
|
||||
import {wait, makeEnv} from '../../helper';
|
||||
import {clearStoresCache} from '../../../src';
|
||||
import moment from 'moment';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
@ -145,3 +146,101 @@ test('Renderer:FormItem:validateApi:failed', async () => {
|
||||
expect(onSubmit).not.toHaveBeenCalled();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Renderer:FormItem:extraName', async () => {
|
||||
const onSubmit = jest.fn();
|
||||
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'form',
|
||||
id: 'theform',
|
||||
body: [
|
||||
{
|
||||
type: 'input-date-range',
|
||||
format: 'YYYY-MM-DD',
|
||||
name: 'begin',
|
||||
extraName: 'end',
|
||||
label: 'Label'
|
||||
}
|
||||
],
|
||||
title: 'The form',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
label: 'ChangeValue',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'setValue',
|
||||
componentId: 'theform',
|
||||
args: {
|
||||
value: {
|
||||
end: `${moment().format('YYYY-MM')}-16`
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
label: 'Submit'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
onSubmit
|
||||
},
|
||||
makeEnv({})
|
||||
)
|
||||
);
|
||||
|
||||
// 打开弹框
|
||||
fireEvent.click(
|
||||
container.querySelector('.cxd-DateRangePicker-input') as HTMLElement
|
||||
);
|
||||
await wait(200);
|
||||
|
||||
// 点击选择
|
||||
fireEvent.click(
|
||||
container.querySelector(
|
||||
'.cxd-DateRangePicker-popover tr td[data-value="15"]'
|
||||
) as HTMLElement
|
||||
);
|
||||
|
||||
// 点击选择
|
||||
fireEvent.click(
|
||||
container.querySelector(
|
||||
'.cxd-DateRangePicker-popover tr td[data-value="15"]'
|
||||
) as HTMLElement
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('确认'));
|
||||
|
||||
fireEvent.click(getByText('Submit'));
|
||||
await wait(300);
|
||||
|
||||
expect(onSubmit).toBeCalledTimes(1);
|
||||
expect(onSubmit.mock.calls[0][0]).toMatchObject({
|
||||
begin: `${moment().format('YYYY-MM')}-15`,
|
||||
end: `${moment().format('YYYY-MM')}-15`
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('ChangeValue'));
|
||||
await wait(200);
|
||||
expect(
|
||||
(container.querySelector('input[placeholder="结束时间"]') as any).value
|
||||
).toBe(`${moment().format('YYYY-MM')}-16`);
|
||||
|
||||
fireEvent.click(getByText('Submit'));
|
||||
await wait(300);
|
||||
|
||||
expect(onSubmit).toBeCalledTimes(2);
|
||||
expect(onSubmit.mock.calls[1][0]).toMatchObject({
|
||||
begin: `${moment().format('YYYY-MM')}-15`,
|
||||
end: `${moment().format('YYYY-MM')}-16`
|
||||
});
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user