mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 10:59:42 +08:00
Merge remote-tracking branch 'baidu/master' into feat/table-expandme-checkme-toggle-entire-tree
This commit is contained in:
commit
5108906e96
@ -287,7 +287,7 @@ order: 38
|
||||
| step | `number \| string` | `1` | 步长,支持变量 | `3.3.0`后支持变量 |
|
||||
| showSteps | `boolean` | `false` | 是否显示步长 |
|
||||
| parts | `number` or `number[]` | `1` | 分割的块数<br/>主持数组传入分块的节点 |
|
||||
| marks | <code>{ [number | string]: ReactNode }</code> or <code>{ [number | string]: { style: CSSProperties, label: ReactNode } }</code> | | 刻度标记<br/>- 支持自定义样式<br/>- 设置百分比 |
|
||||
| marks | <code>{ [number | string]: string | number | SchemaObject }</code> or <code>{ [number | string]: { style: CSSProperties, label: string } }</code> | | 刻度标记<br/>- 支持自定义样式<br/>- 设置百分比 |
|
||||
| tooltipVisible | `boolean` | `false` | 是否显示滑块标签 |
|
||||
| tooltipPlacement | `auto` or `bottom` or `left` or `right` | `top` | 滑块标签的位置,默认`auto`,方向自适应<br/>前置条件:tooltipVisible 不为 false 时有效 |
|
||||
| tipFormatter | `function` | | 控制滑块标签显隐函数<br/>前置条件:tooltipVisible 不为 false 时有效 |
|
||||
|
@ -917,6 +917,11 @@ order: 54
|
||||
| deleteSuccess | `index: number` 所在行记录索引 <br /> `item: object` 所在行记录 <br/> `[name]: object[]`列表记录 | 配置了`deleteApi`,调用接口成功时触发 |
|
||||
| deleteFail | `index: number` 所在行记录索引 <br /> `item: object` 所在行记录 <br/> `[name]: object[]`列表记录<br />`error: object` `deleteApi`请求失败后返回的错误信息 | 配置了`deleteApi`,调用接口失败时触发 |
|
||||
| change | `[name]: object[]` 列表记录 | 组件数据发生改变时触发 |
|
||||
| orderChange | `movedItems: item[]` 已排序数据 | 手动拖拽行排序时触发 |
|
||||
| rowClick | `item: object` 行点击数据<br/>`index: number` 行索引 | 单击整行时触发 |
|
||||
| rowDbClick | `item: object` 行点击数据<br/>`index: number` 行索引 | 双击整行时触发 |
|
||||
| rowMouseEnter | `item: object` 行移入数据<br/>`index: number` 行索引 | 移入整行时触发 |
|
||||
| rowMouseLeave | `item: object` 行移出数据<br/>`index: number` 行索引 | 移出整行时触发 |
|
||||
|
||||
### add
|
||||
|
||||
@ -1563,6 +1568,287 @@ order: 54
|
||||
}
|
||||
```
|
||||
|
||||
### orderChange
|
||||
|
||||
在开启拖拽排序行记录后才会用到,排序确认后触发。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"id": 1,
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"showIndex": true,
|
||||
"type": "input-table",
|
||||
"name": "table",
|
||||
"columns": [
|
||||
{
|
||||
"name": "a",
|
||||
"label": "A"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"label": "B"
|
||||
}
|
||||
],
|
||||
"addable": true,
|
||||
"draggable": true,
|
||||
"onEvent": {
|
||||
"orderChange": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "${event.data.movedItems.length|json}行发生移动"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### rowClick
|
||||
|
||||
点击行记录。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"id": 1,
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"showIndex": true,
|
||||
"type": "input-table",
|
||||
"name": "table",
|
||||
"columns": [
|
||||
{
|
||||
"name": "a",
|
||||
"label": "A"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"label": "B"
|
||||
}
|
||||
],
|
||||
"addable": true,
|
||||
"onEvent": {
|
||||
"rowClick": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "行单击数据:${event.data.item|json};行索引:${event.data.index}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### rowDbClick
|
||||
|
||||
双击行记录。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"id": 1,
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"showIndex": true,
|
||||
"type": "input-table",
|
||||
"name": "table",
|
||||
"columns": [
|
||||
{
|
||||
"name": "a",
|
||||
"label": "A"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"label": "B"
|
||||
}
|
||||
],
|
||||
"addable": true,
|
||||
"onEvent": {
|
||||
"rowDbClick": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "行单击数据:${event.data.item|json};行索引:${event.data.index}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### rowMouseEnter
|
||||
|
||||
鼠标移入行记录。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"id": 1,
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"showIndex": true,
|
||||
"type": "input-table",
|
||||
"name": "table",
|
||||
"columns": [
|
||||
{
|
||||
"name": "a",
|
||||
"label": "A"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"label": "B"
|
||||
}
|
||||
],
|
||||
"addable": true,
|
||||
"onEvent": {
|
||||
"rowMouseEnter": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "行索引:${event.data.index}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### rowMouseLeave
|
||||
|
||||
鼠标移出行记录。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"id": 1,
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"showIndex": true,
|
||||
"type": "input-table",
|
||||
"name": "table",
|
||||
"columns": [
|
||||
{
|
||||
"name": "a",
|
||||
"label": "A"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"label": "B"
|
||||
}
|
||||
],
|
||||
"addable": true,
|
||||
"onEvent": {
|
||||
"rowMouseLeave": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "行索引:${event.data.index}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 动作表
|
||||
|
||||
当前组件对外暴露以下特性动作,其他组件可以通过指定 actionType: 动作名称、componentId: 该组件 id 来触发这些动作,动作配置可以通过 args: {动作配置项名称: xxx}来配置具体的参数,详细请查看事件动作。
|
||||
@ -1574,6 +1860,7 @@ order: 54
|
||||
| setValue | `value: object \| Array<object>` 替换的值<br /> `index?: number` 可选,替换第几行数据,如果没有指定,则替换全部表格数据 | 替换表格数据 |
|
||||
| clear | - | 清空表格数据 |
|
||||
| reset | - | 将表格数据重置为`resetValue`,若没有配置`resetValue`,则清空表格数据 |
|
||||
| initDrag | - | 开启表格拖拽排序功能 |
|
||||
|
||||
### addItem
|
||||
|
||||
@ -2199,3 +2486,83 @@ order: 54
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### initDrag
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "button",
|
||||
"label": "开始表格排序",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"componentId": "drag-input-table",
|
||||
"actionType": "initDrag"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "input-table",
|
||||
"label": "表格表单",
|
||||
"id": "drag-input-table",
|
||||
"name": "table",
|
||||
"columns": [
|
||||
{
|
||||
"name": "a",
|
||||
"label": "A"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"label": "B"
|
||||
}
|
||||
],
|
||||
"addable": true,
|
||||
"footerAddBtn": {
|
||||
"label": "新增",
|
||||
"icon": "fa fa-plus",
|
||||
"hidden": true
|
||||
},
|
||||
"strictMode": true,
|
||||
"minLength": 0,
|
||||
"needConfirm": false,
|
||||
"showTableAddBtn": false
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"id": 1,
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"a": "a3",
|
||||
"b": "b3"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"a": "a4",
|
||||
"b": "b4"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"a": "a5",
|
||||
"b": "b5"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -878,12 +878,95 @@ icon:
|
||||
}
|
||||
```
|
||||
|
||||
## 分页
|
||||
|
||||
> `3.6.0`及以上版本
|
||||
|
||||
当数据量庞大时,可以开启数据源分页,此时左侧列表底部会出现分页控件,相关配置参考属性表。通常在提交表单中使用分页场景,处理数据量较大的数据源。如果需要在表单中回显已选值,建议同时设置`{"joinValues": false, "extractValue": false}`,因为已选数据可能位于不同的分页,如果仅使用value值作为提交值,可能会导致右侧结果区无法正确渲染。
|
||||
|
||||
> 仅列表(list)和表格(table)展示模式支持分页,接口的数据结构参考[CRUD数据源接口格式](../crud#数据结构)
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"label": "默认",
|
||||
"type": "transfer",
|
||||
"name": "transfer",
|
||||
"joinValues": false,
|
||||
"extractValue": false,
|
||||
"source": "/api/mock2/options/transfer?page=${page}&perPage=${perPage}",
|
||||
"pagination": {
|
||||
"enable": true,
|
||||
"layout": ["pager", "perpage", "total"],
|
||||
"popOverContainerSelector": ".cxd-Panel--form"
|
||||
},
|
||||
"value": [
|
||||
{"label": "Laura Lewis", "value": "1", "id": 1},
|
||||
{"label": "Christopher Rodriguez", "value": "3", "id": 3},
|
||||
{"label": "Laura Miller", "value": "12", "id": 12},
|
||||
{"label": "Patricia Robinson", "value": "14", "id": 14}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 前端分页
|
||||
|
||||
> `3.6.0`及以上版本
|
||||
|
||||
当使用数据域变量作为数据源时,支持实现前端一次性加载并分页
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"type": "service",
|
||||
"api": {
|
||||
"url": "/api/mock2/options/loadDataOnce",
|
||||
"method": "get",
|
||||
"responseData": {
|
||||
"transferOptions": "${items}"
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"label": "默认",
|
||||
"type": "transfer",
|
||||
"name": "transfer",
|
||||
"joinValues": false,
|
||||
"extractValue": false,
|
||||
"source": "${transferOptions}",
|
||||
"pagination": {
|
||||
"enable": true,
|
||||
"layout": ["pager", "perpage", "total"],
|
||||
"popOverContainerSelector": ".cxd-Panel--form"
|
||||
},
|
||||
"value": [
|
||||
{"label": "Laura Lewis", "value": "1", "id": 1},
|
||||
{"label": "Christopher Rodriguez", "value": "3", "id": 3},
|
||||
{"label": "Laura Miller", "value": "12", "id": 12},
|
||||
{"label": "Patricia Robinson", "value": "14", "id": 14}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| -------------------------- | ----------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| -------------------------- | ----------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- |
|
||||
| options | `Array<object>`或`Array<string>` | | [选项组](./options#%E9%9D%99%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-options) |
|
||||
| source | `string`或 [API](../../../docs/types/api) | | [动态选项组](./options#%E5%8A%A8%E6%80%81%E9%80%89%E9%A1%B9%E7%BB%84-source) |
|
||||
| delimeter | `string` | `false` | [拼接符](./options#%E6%8B%BC%E6%8E%A5%E7%AC%A6-delimiter) |
|
||||
@ -909,6 +992,13 @@ icon:
|
||||
| valueTpl | `string` \| [SchemaNode](../../docs/types/schemanode) | | 用来自定义值的展示 |
|
||||
| itemHeight | `number` | `32` | 每个选项的高度,用于虚拟渲染 |
|
||||
| virtualThreshold | `number` | `100` | 在选项数量超过多少时开启虚拟渲染 |
|
||||
| pagination | `object` | | 分页配置 | `3.6.0` |
|
||||
| pagination.className | `string` | | 分页控件CSS类名 | `3.6.0` |
|
||||
| pagination.enable | `boolean` | | 是否开启分页 | `3.6.0` |
|
||||
| pagination.layout | `string` \| `string[]` | `["pager"]` | 通过控制 layout 属性的顺序,调整分页结构布局 | `3.6.0` |
|
||||
| pagination.perPageAvailable | `number[]` | `[10, 20, 50, 100]` | 指定每页可以显示多少条 | `3.6.0` |
|
||||
| pagination.maxButtons | `number` | `5` | 最多显示多少个分页按钮,最小为 5 | `3.6.0` |
|
||||
| pagination.popOverContainerSelector | `string` | | 切换每页条数的控件挂载点 | `3.6.0` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -433,31 +433,33 @@ List 的内容、Card 卡片的内容配置同上
|
||||
|
||||
## 属性表
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ------------------ | ------------------------------------ | --------- | --------------------------------------------------------------------------------------------- | ------- |
|
||||
| type | `string` | | 如果在 Table、Card 和 List 中,为`"image"`;在 Form 中用作静态展示,为`"static-image"` |
|
||||
| className | `string` | | 外层 CSS 类名 |
|
||||
| innerClassName | `string` | | 组件内层 CSS 类名 |
|
||||
| imageClassName | `string` | | 图片 CSS 类名 |
|
||||
| thumbClassName | `string` | | 图片缩率图 CSS 类名 |
|
||||
| height | `string` | | 图片缩率高度 |
|
||||
| width | `string` | | 图片缩率宽度 |
|
||||
| title | `string` | | 标题 |
|
||||
| imageCaption | `string` | | 描述 |
|
||||
| placeholder | `string` | | 占位文本 |
|
||||
| defaultImage | `string` | | 无数据时显示的图片 |
|
||||
| src | `string` | | 缩略图地址 |
|
||||
| href | [模板](../../docs/concepts/template) | | 外部链接地址 |
|
||||
| originalSrc | `string` | | 原图地址 |
|
||||
| enlargeAble | `boolean` | | 支持放大预览 |
|
||||
| enlargeTitle | `string` | | 放大预览的标题 |
|
||||
| enlargeCaption | `string` | | 放大预览的描述 |
|
||||
| enlargeWithGallary | `string` | `true` | 在表格中,图片的放大功能会默认展示所有图片信息,设置为`false`将关闭放大模式下图片集列表的展示 |
|
||||
| thumbMode | `string` | `contain` | 预览图模式,可选:`'w-full'`, `'h-full'`, `'contain'`, `'cover'` |
|
||||
| thumbRatio | `string` | `1:1` | 预览图比例,可选:`'1:1'`, `'4:3'`, `'16:9'` |
|
||||
| imageMode | `string` | `thumb` | 图片展示模式,可选:`'thumb'`, `'original'` 即:缩略图模式 或者 原图模式 |
|
||||
| showToolbar | `boolean` | `false` | 放大模式下是否展示图片的工具栏 | `2.2.0` |
|
||||
| toolbarActions | `ImageAction[]` | | 图片工具栏,支持旋转,缩放,默认操作全部开启 | `2.2.0` |
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ------------------ | ------------------------------------------------ | --------- | --------------------------------------------------------------------------------------------- | ------- |
|
||||
| type | `string` | | 如果在 Table、Card 和 List 中,为`"image"`;在 Form 中用作静态展示,为`"static-image"` |
|
||||
| className | `string` | | 外层 CSS 类名 |
|
||||
| innerClassName | `string` | | 组件内层 CSS 类名 |
|
||||
| imageClassName | `string` | | 图片 CSS 类名 |
|
||||
| thumbClassName | `string` | | 图片缩率图 CSS 类名 |
|
||||
| height | `string` | | 图片缩率高度 |
|
||||
| width | `string` | | 图片缩率宽度 |
|
||||
| title | `string` | | 标题 |
|
||||
| imageCaption | `string` | | 描述 |
|
||||
| placeholder | `string` | | 占位文本 |
|
||||
| defaultImage | `string` | | 无数据时显示的图片 |
|
||||
| src | `string` | | 缩略图地址 |
|
||||
| href | [模板](../../docs/concepts/template) | | 外部链接地址 |
|
||||
| originalSrc | `string` | | 原图地址 |
|
||||
| enlargeAble | `boolean` | | 支持放大预览 |
|
||||
| enlargeTitle | `string` | | 放大预览的标题 |
|
||||
| enlargeCaption | `string` | | 放大预览的描述 |
|
||||
| enlargeWithGallary | `string` | `true` | 在表格中,图片的放大功能会默认展示所有图片信息,设置为`false`将关闭放大模式下图片集列表的展示 |
|
||||
| thumbMode | `string` | `contain` | 预览图模式,可选:`'w-full'`, `'h-full'`, `'contain'`, `'cover'` |
|
||||
| thumbRatio | `string` | `1:1` | 预览图比例,可选:`'1:1'`, `'4:3'`, `'16:9'` |
|
||||
| imageMode | `string` | `thumb` | 图片展示模式,可选:`'thumb'`, `'original'` 即:缩略图模式 或者 原图模式 |
|
||||
| showToolbar | `boolean` | `false` | 放大模式下是否展示图片的工具栏 | `2.2.0` |
|
||||
| toolbarActions | `ImageAction[]` | | 图片工具栏,支持旋转,缩放,默认操作全部开启 | `2.2.0` |
|
||||
| maxScale | `number` 或 [模板](../../docs/concepts/template) | | 执行调整图片比例动作时的最大百分比 | `3.4.4` |
|
||||
| minScale | `number` 或 [模板](../../docs/concepts/template) | | 执行调整图片比例动作时的最小百分比 | `3.4.4` |
|
||||
|
||||
#### ImageAction
|
||||
|
||||
@ -475,3 +477,170 @@ interface ImageAction {
|
||||
disabled?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## 事件表
|
||||
|
||||
当前组件会对外派发以下事件,可以通过`onEvent`来监听这些事件,并通过`actions`来配置执行的动作,在`actions`中可以通过`${事件参数名}`或`${event.data.[事件参数名]}`来获取事件产生的数据,详细查看[事件动作](../../docs/concepts/event-action)。
|
||||
|
||||
| 事件名称 | 事件参数 | 说明 |
|
||||
| ---------- | ---------- | -------------- |
|
||||
| click | 上下文数据 | 点击图片时触发 |
|
||||
| mouseenter | 上下文数据 | 鼠标移入时触发 |
|
||||
| mouseleave | 上下文数据 | 鼠标移入时触发 |
|
||||
|
||||
### click / mouseenter / mouseleave
|
||||
|
||||
点击图片 / 鼠标移入图片 / 鼠标移出图片,可以尝试通过${event.context.nativeEvent}获取鼠标事件对象。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "image",
|
||||
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msg": "图片被点击了"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"mouseenter": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msg": "鼠标移入图片"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"mouseleave": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msg": "鼠标移出图片"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 动作表
|
||||
|
||||
当前组件对外暴露以下特性动作,其他组件可以通过指定`actionType: 动作名称`、`componentId: 该组件id`来触发这些动作,动作配置可以通过`args: {动作配置项名称: xxx}`来配置具体的参数,详细请查看[事件动作](../../docs/concepts/event-action#触发其他组件的动作)。
|
||||
|
||||
| 动作名称 | 动作配置 | 说明 |
|
||||
| -------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
|
||||
| preview | - | 预览图片 |
|
||||
| zoom | `scale: number` 或 `scale: `[模板](../../docs/concepts/template),定义每次放大或缩小图片的百分比大小,正值为放大,负值为缩小,默认 50 | 调整图片比例,将图片等比例放大或缩小 |
|
||||
|
||||
### preview
|
||||
|
||||
预览图片,可以通过配置`originalSrc`来指定预览的原图地址。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "page",
|
||||
"body": {
|
||||
"type": "container",
|
||||
"body": [
|
||||
{
|
||||
"type": "container",
|
||||
"body": [
|
||||
{
|
||||
"type": "image",
|
||||
"className": "mb-1",
|
||||
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
|
||||
"originalSrc": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg",
|
||||
"id": "previewImage"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"label": "预览图片",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "preview",
|
||||
"componentId": "previewImage"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### zoom
|
||||
|
||||
调整图片比例,将图片等比例放大或缩小。可以通过配置图片的`maxScale`和`minScale`来限制调整的比例。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "page",
|
||||
"body": {
|
||||
"type": "container",
|
||||
"body": [
|
||||
{
|
||||
"type": "flex",
|
||||
"items": [
|
||||
{
|
||||
"type": "image",
|
||||
"innerClassName": "no-border",
|
||||
"className": "mt-5 mb-5",
|
||||
"src": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80",
|
||||
"maxScale": 200,
|
||||
"minScale": 20,
|
||||
"id": "zoomImage"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"label": "放大图片",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "zoom",
|
||||
"args": {
|
||||
"scale": 50,
|
||||
},
|
||||
"componentId": "zoomImage"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"label": "缩小图片",
|
||||
"className": "mx-1",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "zoom",
|
||||
"args": {
|
||||
"scale": -50,
|
||||
},
|
||||
"componentId": "zoomImage"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -81,6 +81,30 @@ order: 58
|
||||
|
||||
可以使用 `![text](video.mp4)` 语法来嵌入视频。
|
||||
|
||||
## 支持 latex
|
||||
|
||||
> 3.6.0 及以上版本
|
||||
|
||||
公式渲染使用 KaTeX 实现,由于体积太大默认不提供,需要自己去[下载](https://github.com/KaTeX/KaTeX/releases),在页面中引入以下三个文件:
|
||||
|
||||
```
|
||||
<link rel="stylesheet" href="katex/katex.min.css">
|
||||
<script src="katex/katex.min.js"></script>
|
||||
<script src="katex/contrib/auto-render.min.js"></script>
|
||||
```
|
||||
|
||||
markdown 中的 `$` 或 `$$` 包裹的内容就能以公式展现,比如 `$\sqrt{a^2 + b^2}$`,如果是在代码中 `\` 要转义为 `\\`
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": {
|
||||
"type": "markdown",
|
||||
"value": "$$\\hat{f} (\\xi)=\\int_{-\\infty}^{\\infty}f(x)e^{-2\\pi ix\\xi}dx$$"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 高级配置
|
||||
|
||||
> 1.8.1 及以上版本
|
||||
|
@ -68,9 +68,111 @@ order: 73
|
||||
| disabled | `boolean` | false | 是否禁用 |
|
||||
| onPageChange | page、perPage 改变时会触发 | (page: number, perPage: number) => void; | 分页改变触发 |
|
||||
|
||||
## 事件表
|
||||
|
||||
当前组件会对外派发以下事件,可以通过`onEvent`来监听这些事件,并通过`actions`来配置执行的动作,在`actions`中可以通过`${事件参数名}`或`${event.data.[事件参数名]}`来获取事件产生的数据,详细请查看[事件动作](../../docs/concepts/event-action)。
|
||||
|
||||
> `[name]`表示当前组件绑定的名称,即`name`属性,如果没有配置`name`属性,则通过`value`取值。
|
||||
> | 事件名称 | 事件参数 | 说明 |
|
||||
> | -------- | ------------------------------------- | ------------------------------------------- |
|
||||
> | change | `[value]: object` 当前页码的值<br/> | 当前页码值改变时触发 |
|
||||
> | change | `page: number` 当前页码的值<br/>`perPage: number` 每页显示的记录数 | 当前页码值改变时触发 |
|
||||
|
||||
### change
|
||||
|
||||
切换页码时,通过更新 service 数据域中的 page 来实现联动刷新 table 表格数据。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
"id": "service_01",
|
||||
"api": "/api/mock2/crud/table?page=${page}",
|
||||
"data": {
|
||||
"page": 1
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "table",
|
||||
"title": "表格1",
|
||||
"source": "$rows",
|
||||
"columns": [
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"label": "Version"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "pagination",
|
||||
"activePage": "${page}",
|
||||
"hasNext": true,
|
||||
"onEvent": {
|
||||
"change": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "setValue",
|
||||
"componentId": "service_01",
|
||||
"args": {
|
||||
"value": {
|
||||
"page": "${event.data.page}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
切换页码时,通过向 service 发送 page 并重新加载 service 数据来实现联动刷新 table 表格数据。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
"id": "service_02",
|
||||
"api": "/api/mock2/crud/table?page=${page}",
|
||||
"data": {
|
||||
"page": 1
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "table",
|
||||
"title": "表格1",
|
||||
"source": "$rows",
|
||||
"columns": [
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"label": "Version"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "pagination",
|
||||
"activePage": "${page}",
|
||||
"hasNext": true,
|
||||
"onEvent": {
|
||||
"change": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "reload",
|
||||
"componentId": "service_02",
|
||||
"data": {
|
||||
"page": "${event.data.page}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
@ -2324,6 +2324,70 @@ popOver 的其它配置请参考 [popover](./popover)
|
||||
}
|
||||
```
|
||||
|
||||
### rowDbClick
|
||||
|
||||
双击整行时触发。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
"api": "/api/mock2/sample?perPage=10",
|
||||
"body": [
|
||||
{
|
||||
"type": "table",
|
||||
"source": "$rows",
|
||||
"onEvent": {
|
||||
"rowDbClick": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "行单击数据:${event.data.item|json};行索引:${event.data.index}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"label": "ID",
|
||||
"searchable": true
|
||||
},
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Rendering engine",
|
||||
"filterable": {
|
||||
"options": [
|
||||
"Internet Explorer 4.0",
|
||||
"Internet Explorer 5.0"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "browser",
|
||||
"label": "Browser",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"name": "platform",
|
||||
"label": "Platform(s)"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"label": "Engine version"
|
||||
},
|
||||
{
|
||||
"name": "grade",
|
||||
"label": "CSS grade"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### rowMouseEnter
|
||||
|
||||
鼠标移入行记录。
|
||||
|
@ -75,42 +75,42 @@ order: 9
|
||||
|
||||
```schema
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
type: 'button',
|
||||
label: '尝试点击、鼠标移入/移出',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
"type": "button",
|
||||
"label": "尝试点击、鼠标移入/移出",
|
||||
"level": "primary",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msgType: 'info',
|
||||
msg: '派发点击事件'
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "派发点击事件"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
mouseenter: {
|
||||
actions: [
|
||||
"mouseenter": {
|
||||
"actions": [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msgType: 'info',
|
||||
msg: '派发鼠标移入事件'
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "派发鼠标移入事件"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
mouseleave: {
|
||||
actions: [
|
||||
"mouseleave": {
|
||||
"actions": [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msgType: 'info',
|
||||
msg: '派发鼠标移出事件'
|
||||
"actionType": "toast",
|
||||
"args": {
|
||||
"msgType": "info",
|
||||
"msg": "派发鼠标移出事件"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -24,6 +24,7 @@
|
||||
rel="stylesheet"
|
||||
href="/node_modules/@fortawesome/fontawesome-free/css/v4-shims.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="/node_modules/katex/dist/katex.min.css" />
|
||||
<link rel="stylesheet" href="/node_modules/prismjs/themes/prism.css" />
|
||||
<link rel="stylesheet" href="/examples/doc.css" />
|
||||
|
||||
@ -93,5 +94,10 @@
|
||||
const initialState = {};
|
||||
bootstrap(document.getElementById('root'), initialState);
|
||||
</script>
|
||||
<script defer src="/node_modules/katex/dist/katex.min.js"></script>
|
||||
<script
|
||||
defer
|
||||
src="/node_modules/katex/dist/contrib/auto-render.min.js"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
|
238
mock/cfc/mock/options/loadDataOnce.js
Normal file
238
mock/cfc/mock/options/loadDataOnce.js
Normal file
@ -0,0 +1,238 @@
|
||||
/** 前端分页的接口 */
|
||||
module.exports = function (req, res) {
|
||||
res.json({
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
count: data.length,
|
||||
items: data
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const data = [
|
||||
{
|
||||
"label": "Laura Lewis",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"label": "David Gonzalez",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Rodriguez",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"label": "Sarah Young",
|
||||
"value": "4"
|
||||
},
|
||||
{
|
||||
"label": "James Jones",
|
||||
"value": "5"
|
||||
},
|
||||
{
|
||||
"label": "Larry Robinson",
|
||||
"value": "6"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Perez",
|
||||
"value": "7"
|
||||
},
|
||||
{
|
||||
"label": "Sharon Davis",
|
||||
"value": "8"
|
||||
},
|
||||
{
|
||||
"label": "Kenneth Anderson",
|
||||
"value": "9"
|
||||
},
|
||||
{
|
||||
"label": "Deborah Lewis",
|
||||
"value": "10"
|
||||
},
|
||||
{
|
||||
"label": "Jennifer Lewis",
|
||||
"value": "11"
|
||||
},
|
||||
{
|
||||
"label": "Laura Miller",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"label": "Larry Harris",
|
||||
"value": "13"
|
||||
},
|
||||
{
|
||||
"label": "Patricia Robinson",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"label": "Mark Davis",
|
||||
"value": "15"
|
||||
},
|
||||
{
|
||||
"label": "Jessica Harris",
|
||||
"value": "16"
|
||||
},
|
||||
{
|
||||
"label": "Anna Brown",
|
||||
"value": "17"
|
||||
},
|
||||
{
|
||||
"label": "Lisa Young",
|
||||
"value": "18"
|
||||
},
|
||||
{
|
||||
"label": "Donna Williams",
|
||||
"value": "19"
|
||||
},
|
||||
{
|
||||
"label": "Shirley Davis",
|
||||
"value": "20"
|
||||
},
|
||||
{
|
||||
"label": "Richard Clark",
|
||||
"value": "21"
|
||||
},
|
||||
{
|
||||
"label": "Cynthia Martinez",
|
||||
"value": "22"
|
||||
},
|
||||
{
|
||||
"label": "Kimberly Walker",
|
||||
"value": "23"
|
||||
},
|
||||
{
|
||||
"label": "Timothy Anderson",
|
||||
"value": "24"
|
||||
},
|
||||
{
|
||||
"label": "Betty Lee",
|
||||
"value": "25"
|
||||
},
|
||||
{
|
||||
"label": "Jeffrey Allen",
|
||||
"value": "26"
|
||||
},
|
||||
{
|
||||
"label": "Karen Martinez",
|
||||
"value": "27"
|
||||
},
|
||||
{
|
||||
"label": "Anna Lopez",
|
||||
"value": "28"
|
||||
},
|
||||
{
|
||||
"label": "Dorothy Anderson",
|
||||
"value": "29"
|
||||
},
|
||||
{
|
||||
"label": "David Perez",
|
||||
"value": "30"
|
||||
},
|
||||
{
|
||||
"label": "Dorothy Martin",
|
||||
"value": "31"
|
||||
},
|
||||
{
|
||||
"label": "George Johnson",
|
||||
"value": "32"
|
||||
},
|
||||
{
|
||||
"label": "Donald Jackson",
|
||||
"value": "33"
|
||||
},
|
||||
{
|
||||
"label": "Mary Brown",
|
||||
"value": "34"
|
||||
},
|
||||
{
|
||||
"label": "Deborah Martinez",
|
||||
"value": "35"
|
||||
},
|
||||
{
|
||||
"label": "Donald Jackson",
|
||||
"value": "36"
|
||||
},
|
||||
{
|
||||
"label": "Lisa Robinson",
|
||||
"value": "37"
|
||||
},
|
||||
{
|
||||
"label": "Laura Martinez",
|
||||
"value": "38"
|
||||
},
|
||||
{
|
||||
"label": "Timothy Taylor",
|
||||
"value": "39"
|
||||
},
|
||||
{
|
||||
"label": "Joseph Martinez",
|
||||
"value": "40"
|
||||
},
|
||||
{
|
||||
"label": "Karen Wilson",
|
||||
"value": "41"
|
||||
},
|
||||
{
|
||||
"label": "Karen Walker",
|
||||
"value": "42"
|
||||
},
|
||||
{
|
||||
"label": "William Martinez",
|
||||
"value": "43"
|
||||
},
|
||||
{
|
||||
"label": "Linda Brown",
|
||||
"value": "44"
|
||||
},
|
||||
{
|
||||
"label": "Elizabeth Brown",
|
||||
"value": "45"
|
||||
},
|
||||
{
|
||||
"label": "Anna Moore",
|
||||
"value": "46"
|
||||
},
|
||||
{
|
||||
"label": "Robert Martinez",
|
||||
"value": "47"
|
||||
},
|
||||
{
|
||||
"label": "Edward Hernandez",
|
||||
"value": "48"
|
||||
},
|
||||
{
|
||||
"label": "Elizabeth Hall",
|
||||
"value": "49"
|
||||
},
|
||||
{
|
||||
"label": "Linda Jackson",
|
||||
"value": "50"
|
||||
},
|
||||
{
|
||||
"label": "Brian Jones",
|
||||
"value": "51"
|
||||
},
|
||||
{
|
||||
"label": "Amy Thompson",
|
||||
"value": "52"
|
||||
},
|
||||
{
|
||||
"label": "Kimberly Wilson",
|
||||
"value": "53"
|
||||
},
|
||||
{
|
||||
"label": "Nancy Garcia",
|
||||
"value": "54"
|
||||
},
|
||||
{
|
||||
"label": "Mary Thompson",
|
||||
"value": "55"
|
||||
}
|
||||
].map(function (item, index) {
|
||||
return Object.assign({}, item, {
|
||||
id: index + 1
|
||||
});
|
||||
});
|
242
mock/cfc/mock/options/transfer.js
Normal file
242
mock/cfc/mock/options/transfer.js
Normal file
@ -0,0 +1,242 @@
|
||||
/** Transfer分页接口 */
|
||||
module.exports = function (req, res) {
|
||||
const perPage = Number(req.query.perPage || 10);
|
||||
const page = Number(req.query.page || 1);
|
||||
|
||||
res.json({
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
count: data.length,
|
||||
page: page,
|
||||
items: data.concat().splice((page - 1) * perPage, perPage)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const data = [
|
||||
{
|
||||
"label": "Laura Lewis",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"label": "David Gonzalez",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Rodriguez",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"label": "Sarah Young",
|
||||
"value": "4"
|
||||
},
|
||||
{
|
||||
"label": "James Jones",
|
||||
"value": "5"
|
||||
},
|
||||
{
|
||||
"label": "Larry Robinson",
|
||||
"value": "6"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Perez",
|
||||
"value": "7"
|
||||
},
|
||||
{
|
||||
"label": "Sharon Davis",
|
||||
"value": "8"
|
||||
},
|
||||
{
|
||||
"label": "Kenneth Anderson",
|
||||
"value": "9"
|
||||
},
|
||||
{
|
||||
"label": "Deborah Lewis",
|
||||
"value": "10"
|
||||
},
|
||||
{
|
||||
"label": "Jennifer Lewis",
|
||||
"value": "11"
|
||||
},
|
||||
{
|
||||
"label": "Laura Miller",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"label": "Larry Harris",
|
||||
"value": "13"
|
||||
},
|
||||
{
|
||||
"label": "Patricia Robinson",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"label": "Mark Davis",
|
||||
"value": "15"
|
||||
},
|
||||
{
|
||||
"label": "Jessica Harris",
|
||||
"value": "16"
|
||||
},
|
||||
{
|
||||
"label": "Anna Brown",
|
||||
"value": "17"
|
||||
},
|
||||
{
|
||||
"label": "Lisa Young",
|
||||
"value": "18"
|
||||
},
|
||||
{
|
||||
"label": "Donna Williams",
|
||||
"value": "19"
|
||||
},
|
||||
{
|
||||
"label": "Shirley Davis",
|
||||
"value": "20"
|
||||
},
|
||||
{
|
||||
"label": "Richard Clark",
|
||||
"value": "21"
|
||||
},
|
||||
{
|
||||
"label": "Cynthia Martinez",
|
||||
"value": "22"
|
||||
},
|
||||
{
|
||||
"label": "Kimberly Walker",
|
||||
"value": "23"
|
||||
},
|
||||
{
|
||||
"label": "Timothy Anderson",
|
||||
"value": "24"
|
||||
},
|
||||
{
|
||||
"label": "Betty Lee",
|
||||
"value": "25"
|
||||
},
|
||||
{
|
||||
"label": "Jeffrey Allen",
|
||||
"value": "26"
|
||||
},
|
||||
{
|
||||
"label": "Karen Martinez",
|
||||
"value": "27"
|
||||
},
|
||||
{
|
||||
"label": "Anna Lopez",
|
||||
"value": "28"
|
||||
},
|
||||
{
|
||||
"label": "Dorothy Anderson",
|
||||
"value": "29"
|
||||
},
|
||||
{
|
||||
"label": "David Perez",
|
||||
"value": "30"
|
||||
},
|
||||
{
|
||||
"label": "Dorothy Martin",
|
||||
"value": "31"
|
||||
},
|
||||
{
|
||||
"label": "George Johnson",
|
||||
"value": "32"
|
||||
},
|
||||
{
|
||||
"label": "Donald Jackson",
|
||||
"value": "33"
|
||||
},
|
||||
{
|
||||
"label": "Mary Brown",
|
||||
"value": "34"
|
||||
},
|
||||
{
|
||||
"label": "Deborah Martinez",
|
||||
"value": "35"
|
||||
},
|
||||
{
|
||||
"label": "Donald Jackson",
|
||||
"value": "36"
|
||||
},
|
||||
{
|
||||
"label": "Lisa Robinson",
|
||||
"value": "37"
|
||||
},
|
||||
{
|
||||
"label": "Laura Martinez",
|
||||
"value": "38"
|
||||
},
|
||||
{
|
||||
"label": "Timothy Taylor",
|
||||
"value": "39"
|
||||
},
|
||||
{
|
||||
"label": "Joseph Martinez",
|
||||
"value": "40"
|
||||
},
|
||||
{
|
||||
"label": "Karen Wilson",
|
||||
"value": "41"
|
||||
},
|
||||
{
|
||||
"label": "Karen Walker",
|
||||
"value": "42"
|
||||
},
|
||||
{
|
||||
"label": "William Martinez",
|
||||
"value": "43"
|
||||
},
|
||||
{
|
||||
"label": "Linda Brown",
|
||||
"value": "44"
|
||||
},
|
||||
{
|
||||
"label": "Elizabeth Brown",
|
||||
"value": "45"
|
||||
},
|
||||
{
|
||||
"label": "Anna Moore",
|
||||
"value": "46"
|
||||
},
|
||||
{
|
||||
"label": "Robert Martinez",
|
||||
"value": "47"
|
||||
},
|
||||
{
|
||||
"label": "Edward Hernandez",
|
||||
"value": "48"
|
||||
},
|
||||
{
|
||||
"label": "Elizabeth Hall",
|
||||
"value": "49"
|
||||
},
|
||||
{
|
||||
"label": "Linda Jackson",
|
||||
"value": "50"
|
||||
},
|
||||
{
|
||||
"label": "Brian Jones",
|
||||
"value": "51"
|
||||
},
|
||||
{
|
||||
"label": "Amy Thompson",
|
||||
"value": "52"
|
||||
},
|
||||
{
|
||||
"label": "Kimberly Wilson",
|
||||
"value": "53"
|
||||
},
|
||||
{
|
||||
"label": "Nancy Garcia",
|
||||
"value": "54"
|
||||
},
|
||||
{
|
||||
"label": "Mary Thompson",
|
||||
"value": "55"
|
||||
}
|
||||
].map(function (item, index) {
|
||||
return Object.assign({}, item, {
|
||||
id: index + 1
|
||||
});
|
||||
});
|
@ -40,9 +40,9 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"postcss": "^8.4.14",
|
||||
"qs": "6.9.7",
|
||||
"path-to-regexp": "^6.2.0"
|
||||
"qs": "6.9.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/generator": "^7.22.9",
|
||||
@ -79,6 +79,7 @@
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"katex": "^0.16.9",
|
||||
"lerna": "^6.6.2",
|
||||
"lint-staged": "^12.1.2",
|
||||
"magic-string": "^0.26.7",
|
||||
|
@ -36,6 +36,19 @@ export class CmptAction implements RendererAction {
|
||||
*/
|
||||
const key = action.componentId || action.componentName;
|
||||
const dataMergeMode = action.dataMergeMode || 'merge';
|
||||
const path = action.args?.path;
|
||||
|
||||
/** 如果args中携带path参数, 则认为是全局变量赋值, 否则认为是组件变量赋值 */
|
||||
if (action.actionType === 'setValue' && path && typeof path === 'string') {
|
||||
const beforeSetData = renderer?.props?.env?.beforeSetData;
|
||||
if (beforeSetData && typeof beforeSetData === 'function') {
|
||||
const res = await beforeSetData(renderer, action, event);
|
||||
|
||||
if (res === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
console.warn('请提供目标组件的componentId或componentName');
|
||||
@ -59,23 +72,6 @@ export class CmptAction implements RendererAction {
|
||||
}
|
||||
|
||||
if (action.actionType === 'setValue') {
|
||||
const beforeSetData = renderer?.props?.env?.beforeSetData;
|
||||
const path = action.args?.path;
|
||||
|
||||
/** 如果args中携带path参数, 则认为是全局变量赋值, 否则认为是组件变量赋值 */
|
||||
if (
|
||||
path &&
|
||||
typeof path === 'string' &&
|
||||
beforeSetData &&
|
||||
typeof beforeSetData === 'function'
|
||||
) {
|
||||
const res = await beforeSetData(renderer, action, event);
|
||||
|
||||
if (res === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (component?.setData) {
|
||||
return component?.setData(
|
||||
action.args?.value,
|
||||
|
@ -87,6 +87,16 @@ export interface RendererEnv {
|
||||
affixOffsetBottom?: number;
|
||||
|
||||
richTextToken: string;
|
||||
|
||||
/**
|
||||
* 默认的选址组件提供商,目前支持仅 baidu
|
||||
*/
|
||||
locationPickerVendor?: string;
|
||||
|
||||
/**
|
||||
* 选址组件的 ak
|
||||
*/
|
||||
locationPickerAK?: string;
|
||||
loadRenderer: (
|
||||
schema: Schema,
|
||||
path: string,
|
||||
|
@ -50,6 +50,7 @@ import {isAlive} from 'mobx-state-tree';
|
||||
|
||||
import type {LabelAlign} from './Item';
|
||||
import {injectObjectChain} from '../utils';
|
||||
import {reaction} from 'mobx';
|
||||
|
||||
export interface FormHorizontal {
|
||||
left?: number;
|
||||
@ -371,6 +372,7 @@ export interface FormProps
|
||||
onFailed?: (reason: string, errors: any) => any;
|
||||
onFinished: (values: object, action: any) => any;
|
||||
onValidate: (values: object, form: any) => any;
|
||||
onValidChange?: (valid: boolean, props: any) => void; // 表单数据合法性变更
|
||||
messages: {
|
||||
fetchSuccess?: string;
|
||||
fetchFailed?: string;
|
||||
@ -443,6 +445,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
'onChange',
|
||||
'onFailed',
|
||||
'onFinished',
|
||||
'onValidate',
|
||||
'onValidChange',
|
||||
'onSaved',
|
||||
'canAccessSuperData',
|
||||
'lazyChange',
|
||||
@ -460,8 +464,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
[propName: string]: Array<() => Promise<any>>;
|
||||
} = {};
|
||||
asyncCancel: () => void;
|
||||
disposeOnValidate: () => void;
|
||||
disposeRulesValidate: () => void;
|
||||
toDispose: Array<() => void> = [];
|
||||
shouldLoadInitApi: boolean = false;
|
||||
timer: ReturnType<typeof setTimeout>;
|
||||
mounted: boolean;
|
||||
@ -532,6 +535,7 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
store,
|
||||
messages: {fetchSuccess, fetchFailed},
|
||||
onValidate,
|
||||
onValidChange,
|
||||
promptPageLeave,
|
||||
env,
|
||||
rules
|
||||
@ -541,49 +545,63 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
|
||||
if (onValidate) {
|
||||
const finalValidate = promisify(onValidate);
|
||||
this.disposeOnValidate = this.addHook(async () => {
|
||||
const result = await finalValidate(store.data, store);
|
||||
this.toDispose.push(
|
||||
this.addHook(async () => {
|
||||
const result = await finalValidate(store.data, store);
|
||||
|
||||
if (result && isObject(result)) {
|
||||
Object.keys(result).forEach(key => {
|
||||
let msg = result[key];
|
||||
const items = store.getItemsByPath(key);
|
||||
if (result && isObject(result)) {
|
||||
Object.keys(result).forEach(key => {
|
||||
let msg = result[key];
|
||||
const items = store.getItemsByPath(key);
|
||||
|
||||
// 没有找到
|
||||
if (!Array.isArray(items) || !items.length) {
|
||||
return;
|
||||
}
|
||||
// 没有找到
|
||||
if (!Array.isArray(items) || !items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 在setError之前,提前把残留的error信息清除掉,否则每次onValidate后都会一直把报错 append 上去
|
||||
items.forEach(item => item.clearError());
|
||||
// 在setError之前,提前把残留的error信息清除掉,否则每次onValidate后都会一直把报错 append 上去
|
||||
items.forEach(item => item.clearError());
|
||||
|
||||
if (msg) {
|
||||
msg = Array.isArray(msg) ? msg : [msg];
|
||||
items.forEach(item => item.addError(msg));
|
||||
}
|
||||
if (msg) {
|
||||
msg = Array.isArray(msg) ? msg : [msg];
|
||||
items.forEach(item => item.addError(msg));
|
||||
}
|
||||
|
||||
delete result[key];
|
||||
});
|
||||
delete result[key];
|
||||
});
|
||||
|
||||
isEmpty(result)
|
||||
? store.clearRestError()
|
||||
: store.setRestError(Object.keys(result).map(key => result[key]));
|
||||
}
|
||||
});
|
||||
isEmpty(result)
|
||||
? store.clearRestError()
|
||||
: store.setRestError(Object.keys(result).map(key => result[key]));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 表单校验结果发生变化时,触发 onValidChange
|
||||
if (onValidChange) {
|
||||
this.toDispose.push(
|
||||
reaction(
|
||||
() => store.valid,
|
||||
valid => onValidChange(valid, this.props)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(rules) && rules.length) {
|
||||
this.disposeRulesValidate = this.addHook(() => {
|
||||
if (!store.valid) {
|
||||
return;
|
||||
}
|
||||
this.toDispose.push(
|
||||
this.addHook(() => {
|
||||
if (!store.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
rules.forEach(
|
||||
item =>
|
||||
!evalExpression(item.rule, store.data) &&
|
||||
store.addRestError(item.message, item.name)
|
||||
);
|
||||
});
|
||||
rules.forEach(
|
||||
item =>
|
||||
!evalExpression(item.rule, store.data) &&
|
||||
store.addRestError(item.message, item.name)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isEffectiveApi(initApi, store.data, initFetch, initFetchOn)) {
|
||||
@ -655,8 +673,8 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
// this.lazyHandleChange.flush();
|
||||
this.lazyEmitChange.cancel();
|
||||
this.asyncCancel && this.asyncCancel();
|
||||
this.disposeOnValidate && this.disposeOnValidate();
|
||||
this.disposeRulesValidate && this.disposeRulesValidate();
|
||||
this.toDispose.forEach(fn => fn());
|
||||
this.toDispose = [];
|
||||
window.removeEventListener('beforeunload', this.beforePageUnload);
|
||||
this.unBlockRouting?.();
|
||||
}
|
||||
@ -836,30 +854,27 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
return this.props.store.validated;
|
||||
}
|
||||
|
||||
validate(
|
||||
async validate(
|
||||
forceValidate?: boolean,
|
||||
throwErrors: boolean = false
|
||||
throwErrors: boolean = false,
|
||||
toastErrors: boolean = true
|
||||
): Promise<boolean> {
|
||||
const {store, dispatchEvent, data, messages, translate: __} = this.props;
|
||||
|
||||
this.flush();
|
||||
return store
|
||||
.validate(
|
||||
this.hooks['validate'] || [],
|
||||
forceValidate,
|
||||
throwErrors,
|
||||
typeof messages?.validateFailed === 'string'
|
||||
? __(filter(messages.validateFailed, store.data))
|
||||
: undefined
|
||||
)
|
||||
.then((result: boolean) => {
|
||||
if (result) {
|
||||
dispatchEvent('validateSucc', data);
|
||||
} else {
|
||||
dispatchEvent('validateError', data);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
const result = await store.validate(
|
||||
this.hooks['validate'] || [],
|
||||
forceValidate,
|
||||
throwErrors,
|
||||
toastErrors === false
|
||||
? ''
|
||||
: typeof messages?.validateFailed === 'string'
|
||||
? __(filter(messages.validateFailed, store.data))
|
||||
: undefined
|
||||
);
|
||||
|
||||
dispatchEvent(result ? 'validateSucc' : 'validateError', data);
|
||||
return result;
|
||||
}
|
||||
|
||||
setErrors(errors: {[propName: string]: string}, tag = 'remote') {
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
FormBaseControl
|
||||
} from './Item';
|
||||
import {IFormItemStore} from '../store/formItem';
|
||||
import {isObject} from 'amis-core';
|
||||
|
||||
export type OptionsControlComponent = React.ComponentType<FormControlProps>;
|
||||
|
||||
@ -230,7 +231,11 @@ export interface OptionsControlProps
|
||||
selectedOptions: Array<Option>;
|
||||
setOptions: (value: Array<any>, skipNormalize?: boolean) => void;
|
||||
setLoading: (value: boolean) => void;
|
||||
reloadOptions: (setError?: boolean) => void;
|
||||
reloadOptions: (
|
||||
setError?: boolean,
|
||||
isInit?: boolean,
|
||||
data?: Record<string, any>
|
||||
) => void;
|
||||
deferLoad: (option: Option) => void;
|
||||
leftDeferLoad: (option: Option, leftOptions: Option) => void;
|
||||
expandTreeOptions: (nodePathArr: any[]) => void;
|
||||
@ -443,15 +448,12 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
);
|
||||
|
||||
if (prevOptions !== options) {
|
||||
formItem.setOptions(
|
||||
normalizeOptions(
|
||||
options || [],
|
||||
undefined,
|
||||
props.valueField || 'value'
|
||||
),
|
||||
this.changeOptionValue,
|
||||
props.data
|
||||
formItem.loadOptionsFromDataScope(
|
||||
props.source as string,
|
||||
props.data,
|
||||
this.changeOptionValue
|
||||
);
|
||||
|
||||
this.normalizeValue();
|
||||
}
|
||||
} else if (
|
||||
@ -792,20 +794,16 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
}
|
||||
|
||||
@autobind
|
||||
reloadOptions(setError?: boolean, isInit = false) {
|
||||
const {source, formItem, data, onChange, setPrinstineValue, valueField} =
|
||||
reloadOptions(setError?: boolean, isInit = false, data = this.props.data) {
|
||||
const {source, formItem, onChange, setPrinstineValue, valueField} =
|
||||
this.props;
|
||||
|
||||
if (formItem && isPureVariable(source as string)) {
|
||||
isAlive(formItem) &&
|
||||
formItem.setOptions(
|
||||
normalizeOptions(
|
||||
resolveVariableAndFilter(source as string, data, '| raw') || [],
|
||||
undefined,
|
||||
valueField
|
||||
),
|
||||
this.changeOptionValue,
|
||||
data
|
||||
formItem.loadOptionsFromDataScope(
|
||||
source as string,
|
||||
data,
|
||||
this.changeOptionValue
|
||||
);
|
||||
return;
|
||||
} else if (!formItem || !isEffectiveApi(source, data)) {
|
||||
|
@ -156,7 +156,8 @@ export function wrapControl<
|
||||
minLength,
|
||||
maxLength,
|
||||
validateOnChange,
|
||||
label
|
||||
label,
|
||||
pagination
|
||||
}
|
||||
} = this.props;
|
||||
|
||||
@ -230,7 +231,8 @@ export function wrapControl<
|
||||
validateOnChange,
|
||||
label,
|
||||
inputGroupControl,
|
||||
extraName
|
||||
extraName,
|
||||
pagination
|
||||
});
|
||||
|
||||
// issue 这个逻辑应该在 combo 里面自己实现。
|
||||
@ -380,7 +382,8 @@ export function wrapControl<
|
||||
'minLength',
|
||||
'maxLength',
|
||||
'label',
|
||||
'extraName'
|
||||
'extraName',
|
||||
'pagination'
|
||||
],
|
||||
prevProps.$schema,
|
||||
props.$schema,
|
||||
|
@ -35,7 +35,8 @@ export const ComboStore = iRendererStore
|
||||
minLength: 0,
|
||||
maxLength: 0,
|
||||
length: 0,
|
||||
activeKey: 0
|
||||
activeKey: 0,
|
||||
memberValidMap: types.optional(types.frozen(), {})
|
||||
})
|
||||
.views(self => {
|
||||
function getForms() {
|
||||
@ -170,13 +171,21 @@ export const ComboStore = iRendererStore
|
||||
self.activeKey = key;
|
||||
}
|
||||
|
||||
function setMemberValid(valid: boolean, index: number) {
|
||||
self.memberValidMap = {
|
||||
...self.memberValidMap,
|
||||
[index]: valid
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
config,
|
||||
setActiveKey,
|
||||
bindUniuqueItem,
|
||||
unBindUniuqueItem,
|
||||
addForm,
|
||||
onChildStoreDispose
|
||||
onChildStoreDispose,
|
||||
setMemberValid
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -161,10 +161,10 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
|
||||
rule => rule.includes(lhs) && rule.includes(rhs)
|
||||
)
|
||||
) {
|
||||
return lhs === rhs;
|
||||
return lhs !== rhs;
|
||||
}
|
||||
|
||||
return lhs == rhs;
|
||||
return lhs != rhs;
|
||||
})
|
||||
) {
|
||||
if (query[pageField || 'page']) {
|
||||
|
@ -7,11 +7,13 @@ import {
|
||||
Instance
|
||||
} from 'mobx-state-tree';
|
||||
import isEqualWith from 'lodash/isEqualWith';
|
||||
import uniqWith from 'lodash/uniqWith';
|
||||
import {FormStore, IFormStore} from './form';
|
||||
import {str2rules, validate as doValidate} from '../utils/validations';
|
||||
import {Api, Payload, fetchOptions, ApiObject} from '../types';
|
||||
import {ComboStore, IComboStore, IUniqueGroup} from './combo';
|
||||
import {evalExpression} from '../utils/tpl';
|
||||
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
|
||||
import {buildApi, isEffectiveApi} from '../utils/api';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import {
|
||||
@ -98,6 +100,7 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
joinValues: true,
|
||||
extractValue: false,
|
||||
options: types.optional(types.frozen<Array<any>>(), []),
|
||||
optionsRaw: types.optional(types.frozen<Array<any>>(), []),
|
||||
expressionsInOptions: false,
|
||||
selectFirst: false,
|
||||
autoFill: types.frozen(),
|
||||
@ -113,7 +116,18 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
/** 当前表单项所属的InputGroup父元素, 用于收集InputGroup的子元素 */
|
||||
inputGroupControl: types.optional(types.frozen(), {}),
|
||||
colIndex: types.frozen(),
|
||||
rowIndex: types.frozen()
|
||||
rowIndex: types.frozen(),
|
||||
/** Transfer组件分页模式 */
|
||||
pagination: types.optional(types.frozen(), {
|
||||
enable: false,
|
||||
/** 当前页数 */
|
||||
page: 1,
|
||||
/** 每页显示条数 */
|
||||
perPage: 10,
|
||||
/** 总条数 */
|
||||
total: 0
|
||||
}),
|
||||
accumulatedOptions: types.optional(types.frozen<Array<any>>(), [])
|
||||
})
|
||||
.views(self => {
|
||||
function getForm(): any {
|
||||
@ -175,6 +189,26 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
return getLastOptionValue();
|
||||
},
|
||||
|
||||
/** 数据源接口数据是否开启分页 */
|
||||
get enableSourcePagination(): boolean {
|
||||
return !!self.pagination.enable;
|
||||
},
|
||||
|
||||
/** 数据源接口开启分页时当前页码 */
|
||||
get sourcePageNum(): number {
|
||||
return self.pagination.page ?? 1;
|
||||
},
|
||||
|
||||
/** 数据源接口开启分页时每页显示条数 */
|
||||
get sourcePerPageNum(): number {
|
||||
return self.pagination.perPage ?? 10;
|
||||
},
|
||||
|
||||
/** 数据源接口开启分页时数据总条数 */
|
||||
get sourceTotalNum(): number {
|
||||
return self.pagination.total ?? 0;
|
||||
},
|
||||
|
||||
getSelectedOptions: (
|
||||
value: any = self.tmpValue,
|
||||
nodeValueArray?: any[] | undefined
|
||||
@ -308,7 +342,8 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
minLength,
|
||||
validateOnChange,
|
||||
label,
|
||||
inputGroupControl
|
||||
inputGroupControl,
|
||||
pagination
|
||||
}: {
|
||||
extraName?: string;
|
||||
required?: boolean;
|
||||
@ -338,6 +373,11 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
path: string;
|
||||
[propsName: string]: any;
|
||||
};
|
||||
pagination?: {
|
||||
enable?: boolean;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
};
|
||||
}) {
|
||||
if (typeof rules === 'string') {
|
||||
rules = str2rules(rules);
|
||||
@ -372,6 +412,15 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
inputGroupControl?.name != null &&
|
||||
(self.inputGroupControl = inputGroupControl);
|
||||
|
||||
if (pagination && isObject(pagination) && !!pagination.enable) {
|
||||
self.pagination = {
|
||||
enable: true,
|
||||
page: pagination.page ? pagination.page || 1 : 1,
|
||||
perPage: pagination.perPage ? pagination.perPage || 10 : 10,
|
||||
total: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
typeof rules !== 'undefined' ||
|
||||
typeof required !== 'undefined' ||
|
||||
@ -556,6 +605,23 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
}
|
||||
}
|
||||
|
||||
function setPagination(params: {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
total?: number;
|
||||
}) {
|
||||
const {page, perPage, total} = params || {};
|
||||
|
||||
if (self.enableSourcePagination) {
|
||||
self.pagination = {
|
||||
...self.pagination,
|
||||
...(page != null && typeof page === 'number' ? {page} : {}),
|
||||
...(perPage != null && typeof perPage === 'number' ? {perPage} : {}),
|
||||
...(total != null && typeof total === 'number' ? {total} : {})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function setOptions(
|
||||
options: Array<object>,
|
||||
onChange?: (value: any) => void,
|
||||
@ -567,6 +633,15 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
options = filterTree(options, item => item);
|
||||
const originOptions = self.options.concat();
|
||||
self.options = options;
|
||||
/** 开启分页后当前选项内容需要累加 */
|
||||
self.accumulatedOptions = self.enableSourcePagination
|
||||
? uniqWith(
|
||||
[...originOptions, ...options],
|
||||
(lhs, rhs) =>
|
||||
lhs[self.valueField ?? 'value'] ===
|
||||
rhs[self.valueField ?? 'value']
|
||||
)
|
||||
: options;
|
||||
syncOptions(originOptions, data);
|
||||
let selectedOptions;
|
||||
|
||||
@ -722,6 +797,14 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
|
||||
options = normalizeOptions(options as any, undefined, self.valueField);
|
||||
|
||||
if (self.enableSourcePagination) {
|
||||
self.pagination = {
|
||||
...self.pagination,
|
||||
page: parseInt(json.data?.page, 10) || 1,
|
||||
total: parseInt(json.data?.total ?? json.data?.count, 10) || 0
|
||||
};
|
||||
}
|
||||
|
||||
if (config?.extendsOptions && self.selectedOptions.length > 0) {
|
||||
self.selectedOptions.forEach((item: any) => {
|
||||
const exited = findTree(
|
||||
@ -752,6 +835,41 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
return json;
|
||||
});
|
||||
|
||||
/**
|
||||
* 从数据域加载选项数据源,注意这里默认source变量解析后是全量的数据源
|
||||
*/
|
||||
function loadOptionsFromDataScope(
|
||||
source: string,
|
||||
ctx: Record<string, any>,
|
||||
onChange?: (value: any) => void
|
||||
) {
|
||||
let options: any[] = resolveVariableAndFilter(source, ctx, '| raw');
|
||||
|
||||
if (!Array.isArray(options)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
options = normalizeOptions(options, undefined, self.valueField);
|
||||
|
||||
if (self.enableSourcePagination) {
|
||||
self.pagination = {
|
||||
...self.pagination,
|
||||
...(ctx?.page ? {page: ctx?.page} : {}),
|
||||
...(ctx?.perPage ? {perPage: ctx?.perPage} : {}),
|
||||
total: options.length
|
||||
};
|
||||
}
|
||||
|
||||
options = options.slice(
|
||||
(self.pagination.page - 1) * self.pagination.perPage,
|
||||
self.pagination.page * self.pagination.perPage
|
||||
);
|
||||
|
||||
setOptions(options, onChange, ctx);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
const loadAutoUpdateData: (
|
||||
api: Api,
|
||||
data?: object,
|
||||
@ -1377,8 +1495,10 @@ export const FormItemStore = StoreNode.named('FormItemStore')
|
||||
setError,
|
||||
addError,
|
||||
clearError,
|
||||
setPagination,
|
||||
setOptions,
|
||||
loadOptions,
|
||||
loadOptionsFromDataScope,
|
||||
deferLoadOptions,
|
||||
deferLoadLeftOptions,
|
||||
expandTreeOptions,
|
||||
|
@ -657,6 +657,11 @@ export interface BaseSchemaWithoutType {
|
||||
* 编辑器配置,运行时可以忽略
|
||||
*/
|
||||
editorSetting?: {
|
||||
/**
|
||||
* 组件行为、用途,如 create、update、remove
|
||||
*/
|
||||
behavior?: string;
|
||||
|
||||
/**
|
||||
* 组件名称,通常是业务名称方便定位
|
||||
*/
|
||||
@ -666,6 +671,8 @@ export interface BaseSchemaWithoutType {
|
||||
* 编辑器假数据,方便展示
|
||||
*/
|
||||
mock?: any;
|
||||
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -304,7 +304,9 @@ export interface RendererInfo extends RendererScaffoldInfo {
|
||||
sharedContext?: Record<string, any>;
|
||||
dialogTitle?: string; //弹窗标题用于弹窗大纲的展示
|
||||
dialogType?: string; //区分确认对话框类型
|
||||
subEditorVariable?: Array<{label: string; children: any}>; // 传递给子编辑器的组件自定义变量,如listSelect的选项名称和值
|
||||
getSubEditorVariable?: (
|
||||
schema?: any
|
||||
) => Array<{label: string; children: any}>; // 传递给子编辑器的组件自定义变量,如listSelect的选项名称和值
|
||||
}
|
||||
|
||||
export type BasicRendererInfo = Omit<
|
||||
@ -1051,7 +1053,7 @@ export abstract class BasePlugin implements PluginInterface {
|
||||
isListComponent: plugin.isListComponent,
|
||||
rendererName: plugin.rendererName,
|
||||
memberImmutable: plugin.memberImmutable,
|
||||
subEditorVariable: plugin.subEditorVariable
|
||||
getSubEditorVariable: plugin.getSubEditorVariable
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export function JSONPipeIn(obj: any, generateId = false, idMap: any = {}): any {
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item, index) => JSONPipeIn(item, generateId));
|
||||
return obj.map((item, index) => JSONPipeIn(item, generateId, idMap));
|
||||
}
|
||||
|
||||
let toUpdate: any = {};
|
||||
@ -1224,7 +1224,10 @@ export async function resolveVariablesFromScope(node: any, manager: any) {
|
||||
// 子编辑器内读取的host节点自定义变量,非数据域方式,如listSelect的选项值
|
||||
let hostNodeVaraibles = [];
|
||||
if (manager?.store?.isSubEditor) {
|
||||
hostNodeVaraibles = manager.config?.hostNode?.info?.subEditorVariable || [];
|
||||
hostNodeVaraibles =
|
||||
manager.config?.hostNode?.info?.getSubEditorVariable?.(
|
||||
manager.config?.hostNode.schema
|
||||
) || [];
|
||||
}
|
||||
|
||||
const variables: VariableItem[] =
|
||||
|
@ -36,7 +36,7 @@ import type {
|
||||
} from 'amis-editor-core';
|
||||
import {normalizeApi} from 'amis-core';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import omit from 'lodash/omit';
|
||||
import findLastIndex from 'lodash/findLastIndex';
|
||||
|
||||
interface ColumnItem {
|
||||
label: string;
|
||||
@ -371,6 +371,9 @@ export class CRUDPlugin extends BasePlugin {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
level: 'primary',
|
||||
editorSetting: {
|
||||
behavior: 'create'
|
||||
},
|
||||
dialog: {
|
||||
title: '新增',
|
||||
body: {
|
||||
@ -385,6 +388,9 @@ export class CRUDPlugin extends BasePlugin {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
level: 'link',
|
||||
editorSetting: {
|
||||
behavior: 'update'
|
||||
},
|
||||
dialog: {
|
||||
title: '编辑',
|
||||
body: {
|
||||
@ -399,6 +405,9 @@ export class CRUDPlugin extends BasePlugin {
|
||||
type: 'button',
|
||||
actionType: 'dialog',
|
||||
level: 'link',
|
||||
editorSetting: {
|
||||
behavior: 'view'
|
||||
},
|
||||
dialog: {
|
||||
title: '查看详情',
|
||||
body: {
|
||||
@ -415,7 +424,10 @@ export class CRUDPlugin extends BasePlugin {
|
||||
level: 'link',
|
||||
className: 'text-danger',
|
||||
confirmText: '确定要删除?',
|
||||
api: 'delete:/xxx/delete'
|
||||
api: 'delete:/xxx/delete',
|
||||
editorSetting: {
|
||||
behavior: 'delete'
|
||||
}
|
||||
},
|
||||
bulkDelete: {
|
||||
type: 'button',
|
||||
@ -423,12 +435,18 @@ export class CRUDPlugin extends BasePlugin {
|
||||
label: '批量删除',
|
||||
actionType: 'ajax',
|
||||
confirmText: '确定要删除?',
|
||||
api: '/xxx/batch-delete'
|
||||
api: '/xxx/batch-delete',
|
||||
editorSetting: {
|
||||
behavior: 'bulkDelete'
|
||||
}
|
||||
},
|
||||
bulkUpdate: {
|
||||
type: 'button',
|
||||
label: '批量编辑',
|
||||
actionType: 'dialog',
|
||||
editorSetting: {
|
||||
behavior: 'bulkUpdate'
|
||||
},
|
||||
dialog: {
|
||||
title: '批量编辑',
|
||||
size: 'md',
|
||||
@ -543,7 +561,7 @@ export class CRUDPlugin extends BasePlugin {
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'features',
|
||||
name: '__features',
|
||||
label: '启用功能',
|
||||
type: 'checkboxes',
|
||||
joinValues: false,
|
||||
@ -575,10 +593,10 @@ export class CRUDPlugin extends BasePlugin {
|
||||
type: 'input-number',
|
||||
label: '每列显示几个字段',
|
||||
value: 3,
|
||||
name: 'filterColumnCount'
|
||||
name: '__filterColumnCount'
|
||||
}
|
||||
],
|
||||
visibleOn: 'data.features && data.features.includes("filter")'
|
||||
visibleOn: "${__features && CONTAINS(__features, 'filter')}"
|
||||
},
|
||||
{
|
||||
name: 'columns',
|
||||
@ -641,33 +659,99 @@ export class CRUDPlugin extends BasePlugin {
|
||||
]
|
||||
}
|
||||
],
|
||||
pipeIn: (value: any) => {
|
||||
const __features = [];
|
||||
// 收集 filter
|
||||
if (value.filter) {
|
||||
__features.push('filter');
|
||||
}
|
||||
// 收集 列操作
|
||||
const lastIndex = findLastIndex(
|
||||
value.columns || [],
|
||||
(item: any) => item.type === 'operation'
|
||||
);
|
||||
if (lastIndex !== -1) {
|
||||
const operBtns: Array<string> = ['update', 'view', 'delete'];
|
||||
(value.columns[lastIndex].buttons || []).forEach((btn: any) => {
|
||||
if (operBtns.includes(btn.editorSetting?.behavior || '')) {
|
||||
__features.push(btn.editorSetting?.behavior);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 收集批量操作
|
||||
if (Array.isArray(value.bulkActions)) {
|
||||
value.bulkActions.forEach((item: any) => {
|
||||
if (item.editorSetting?.behavior) {
|
||||
__features.push(item.editorSetting?.behavior);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 收集新增
|
||||
if (
|
||||
Array.isArray(value.headerToolbar) &&
|
||||
value.headerToolbar.some(
|
||||
(item: any) => item.editorSetting?.behavior === 'create'
|
||||
)
|
||||
) {
|
||||
__features.push('create');
|
||||
}
|
||||
return {
|
||||
...value,
|
||||
__filterColumnCount: value?.filter?.columnCount || 3,
|
||||
__features: __features,
|
||||
__LastFeatures: [...__features]
|
||||
};
|
||||
},
|
||||
pipeOut: (value: any) => {
|
||||
let valueSchema = cloneDeep(value);
|
||||
// 查看/删除 操作,可选择是否使用接口返回值预填充
|
||||
const features: Array<any> = valueSchema.features;
|
||||
const oper: {
|
||||
type: 'operation';
|
||||
label?: string;
|
||||
buttons: Array<ActionSchema>;
|
||||
} = {
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: []
|
||||
};
|
||||
const itemBtns: Array<string> = ['update', 'view', 'delete'];
|
||||
const hasFeatures = get(features, 'length');
|
||||
|
||||
valueSchema.bulkActions = [];
|
||||
/** 统一api格式 */
|
||||
valueSchema.api =
|
||||
typeof valueSchema.api === 'string'
|
||||
? normalizeApi(valueSchema.api)
|
||||
: valueSchema.api;
|
||||
hasFeatures &&
|
||||
features.forEach((item: string) => {
|
||||
if (itemBtns.includes(item)) {
|
||||
let schema;
|
||||
|
||||
const features: string[] = valueSchema.__features;
|
||||
const lastFeatures: string[] = valueSchema.__LastFeatures;
|
||||
const willAddedList = features.filter(
|
||||
item => !lastFeatures.includes(item)
|
||||
);
|
||||
const willRemoveList = lastFeatures.filter(
|
||||
item => !features.includes(item)
|
||||
);
|
||||
|
||||
const operButtons: any[] = [];
|
||||
const operBtns: string[] = ['update', 'view', 'delete'];
|
||||
|
||||
if (!valueSchema.bulkActions) {
|
||||
valueSchema.bulkActions = [];
|
||||
} else {
|
||||
// 删除 未勾选的批量操作
|
||||
valueSchema.bulkActions = valueSchema.bulkActions.filter(
|
||||
(item: any) =>
|
||||
!willRemoveList.includes(item.editorSetting?.behavior)
|
||||
);
|
||||
}
|
||||
|
||||
// 删除 未勾选的 filter
|
||||
if (willRemoveList.includes('filter') && valueSchema.filter) {
|
||||
delete valueSchema.filter;
|
||||
}
|
||||
|
||||
// 删除 未勾选的 新增
|
||||
if (
|
||||
willRemoveList.includes('create') &&
|
||||
Array.isArray(valueSchema.headerToolbar)
|
||||
) {
|
||||
valueSchema.headerToolbar = valueSchema.headerToolbar.filter(
|
||||
(item: any) => item.editorSetting?.behavior !== 'create'
|
||||
);
|
||||
}
|
||||
|
||||
willAddedList.length &&
|
||||
willAddedList.forEach((item: string) => {
|
||||
if (operBtns.includes(item)) {
|
||||
// 列操作按钮
|
||||
let schema;
|
||||
if (item === 'update') {
|
||||
schema = cloneDeep(this.btnSchemas.update);
|
||||
schema.dialog.body.body = value.columns
|
||||
@ -692,9 +776,7 @@ export class CRUDPlugin extends BasePlugin {
|
||||
? valueSchema.api
|
||||
: {...valueSchema.api, method: 'post'};
|
||||
}
|
||||
|
||||
// 添加操作按钮
|
||||
this.addItem(oper.buttons, schema);
|
||||
schema && operButtons.push(schema);
|
||||
} else {
|
||||
// 批量操作
|
||||
if (item === 'bulkUpdate') {
|
||||
@ -730,13 +812,14 @@ export class CRUDPlugin extends BasePlugin {
|
||||
};
|
||||
valueSchema.headerToolbar = [createSchemaBase, 'bulkActions'];
|
||||
}
|
||||
// 查询
|
||||
let keysFilter = Object.keys(valueSchema.filter || {});
|
||||
if (item === 'filter' && !keysFilter.length) {
|
||||
if (valueSchema.filterEnabledList) {
|
||||
valueSchema.filter = {
|
||||
title: '查询条件'
|
||||
};
|
||||
valueSchema.filter.columnCount = value.filterColumnCount;
|
||||
valueSchema.filter.columnCount = value.__filterColumnCount;
|
||||
valueSchema.filter.mode = 'horizontal';
|
||||
valueSchema.filter.body = valueSchema.filterEnabledList.map(
|
||||
(item: any) => {
|
||||
@ -751,10 +834,30 @@ export class CRUDPlugin extends BasePlugin {
|
||||
}
|
||||
}
|
||||
});
|
||||
const hasOperate = valueSchema.columns.find(
|
||||
|
||||
// 处理列操作按钮
|
||||
const lastIndex = findLastIndex(
|
||||
value.columns || [],
|
||||
(item: any) => item.type === 'operation'
|
||||
);
|
||||
hasFeatures && !hasOperate && valueSchema.columns.push(oper);
|
||||
if (lastIndex === -1) {
|
||||
if (operButtons.length) {
|
||||
valueSchema.columns.push({
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
buttons: operButtons
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const operColumn = valueSchema.columns[lastIndex];
|
||||
operColumn.buttons = (operColumn.buttons || [])
|
||||
.filter(
|
||||
(btn: any) =>
|
||||
!willRemoveList.includes(btn.editorSetting?.behavior)
|
||||
)
|
||||
.concat(operButtons);
|
||||
}
|
||||
|
||||
return valueSchema;
|
||||
},
|
||||
canRebuild: true
|
||||
|
@ -20,7 +20,7 @@ export class CalendarPlugin extends BasePlugin {
|
||||
panelTitle = '日历日程';
|
||||
|
||||
description = '展示日历及日程。';
|
||||
docLink = '/amis/zh-CN/components/calendor';
|
||||
docLink = '/amis/zh-CN/components/calendar';
|
||||
tags = ['展示'];
|
||||
|
||||
scaffold = {
|
||||
|
@ -679,7 +679,7 @@ export class ComboControlPlugin extends BasePlugin {
|
||||
}`;
|
||||
let isColumnChild = false;
|
||||
|
||||
if (trigger) {
|
||||
if (trigger && items) {
|
||||
isColumnChild = someTree(items.children, item => item.id === trigger?.id);
|
||||
|
||||
if (isColumnChild) {
|
||||
@ -698,7 +698,7 @@ export class ComboControlPlugin extends BasePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
const pool = items.children.concat();
|
||||
const pool = items?.children?.concat() || [];
|
||||
|
||||
while (pool.length) {
|
||||
const current = pool.shift() as EditorNodeType;
|
||||
|
@ -474,7 +474,7 @@ export class FormPlugin extends BasePlugin {
|
||||
return {
|
||||
type: 'container',
|
||||
className: 'form-item-gap',
|
||||
visibleOn: `data.feat === '${feat.value}' && (!data.dsType || data.dsType === '${builderKey}')`,
|
||||
visibleOn: `$\{feat === '${feat.value}' && (!dsType || dsType === '${builderKey}')}`,
|
||||
body: flatten([
|
||||
builder.makeSourceSettingForm({
|
||||
feat: feat.value,
|
||||
@ -685,26 +685,28 @@ export class FormPlugin extends BasePlugin {
|
||||
const dsSettings = flatten(
|
||||
this.Features.map(feat =>
|
||||
this.dsManager.buildCollectionFromBuilders(
|
||||
(builder, builderKey, index) => ({
|
||||
type: 'container',
|
||||
className: 'form-item-gap',
|
||||
visibleOn: `data.feat === '${
|
||||
feat.value
|
||||
}' && (data.dsType == null ? '${builderKey}' === '${
|
||||
defaultDsType || ApiDSBuilderKey
|
||||
}' : data.dsType === '${builderKey}')`,
|
||||
body: flatten([
|
||||
builder.makeSourceSettingForm({
|
||||
feat: feat.value,
|
||||
renderer: 'form',
|
||||
inScaffold: false,
|
||||
sourceSettings: {
|
||||
renderLabel: true,
|
||||
userOrders: false
|
||||
}
|
||||
})
|
||||
])
|
||||
})
|
||||
(builder, builderKey, index) => {
|
||||
return {
|
||||
type: 'container',
|
||||
className: 'form-item-gap',
|
||||
visibleOn: `$\{feat === '${
|
||||
feat.value
|
||||
}' && (dsType == null ? '${builderKey}' === '${
|
||||
defaultDsType || ApiDSBuilderKey
|
||||
}' : dsType === '${builderKey}')}`,
|
||||
body: flatten([
|
||||
builder.makeSourceSettingForm({
|
||||
feat: feat.value,
|
||||
renderer: 'form',
|
||||
inScaffold: false,
|
||||
sourceSettings: {
|
||||
renderLabel: true,
|
||||
userOrders: false
|
||||
}
|
||||
})
|
||||
])
|
||||
};
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -1,15 +1,21 @@
|
||||
import {isObject} from 'amis';
|
||||
import {
|
||||
EditorNodeType,
|
||||
RendererPluginAction,
|
||||
RendererPluginEvent
|
||||
BasePlugin,
|
||||
defaultValue,
|
||||
getSchemaTpl,
|
||||
tipedLabel,
|
||||
registerEditorPlugin
|
||||
} from 'amis-editor-core';
|
||||
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
|
||||
import {registerEditorPlugin} from 'amis-editor-core';
|
||||
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
|
||||
import {ValidatorTag} from '../../validator';
|
||||
import {getEventControlConfig} from '../../renderer/event-control/helper';
|
||||
|
||||
import type {IFormStore, IFormItemStore} from 'amis-core';
|
||||
import type {
|
||||
EditorNodeType,
|
||||
RendererPluginAction,
|
||||
RendererPluginEvent,
|
||||
BaseEventContext
|
||||
} from 'amis-editor-core';
|
||||
|
||||
export class RangeControlPlugin extends BasePlugin {
|
||||
static id = 'RangeControlPlugin';
|
||||
@ -135,6 +141,19 @@ export class RangeControlPlugin extends BasePlugin {
|
||||
panelTitle = '滑块';
|
||||
|
||||
panelJustify = true;
|
||||
|
||||
filterProps(props: Record<string, any>, node: EditorNodeType) {
|
||||
if (
|
||||
props.marks &&
|
||||
isObject(props.marks) &&
|
||||
props.marks.hasOwnProperty('$$id')
|
||||
) {
|
||||
delete props.marks.$$id;
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
panelBodyCreator = (context: BaseEventContext) => {
|
||||
return getSchemaTpl('tabs', [
|
||||
{
|
||||
|
@ -653,6 +653,132 @@ export class TableControlPlugin extends BasePlugin {
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'orderChange',
|
||||
eventLabel: '行排序',
|
||||
description: '手动拖拽行排序事件',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
movedItems: {
|
||||
type: 'array',
|
||||
title: '已排序记录'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'rowClick',
|
||||
eventLabel: '行单击',
|
||||
description: '点击整行事件',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
title: '当前行记录'
|
||||
},
|
||||
index: {
|
||||
type: 'number',
|
||||
title: '当前行索引'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'rowDbClick',
|
||||
eventLabel: '行双击',
|
||||
description: '双击整行事件',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
title: '当前行记录'
|
||||
},
|
||||
index: {
|
||||
type: 'number',
|
||||
title: '当前行索引'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'rowMouseEnter',
|
||||
eventLabel: '鼠标移入行事件',
|
||||
description: '移入整行时触发',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
title: '当前行记录'
|
||||
},
|
||||
index: {
|
||||
type: 'number',
|
||||
title: '当前行索引'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'rowMouseLeave',
|
||||
eventLabel: '鼠标移出行事件',
|
||||
description: '移出整行时触发',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
title: '当前行记录'
|
||||
},
|
||||
index: {
|
||||
type: 'number',
|
||||
title: '当前行索引'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@ -811,6 +937,11 @@ export class TableControlPlugin extends BasePlugin {
|
||||
actionType: 'clear',
|
||||
actionLabel: '清空',
|
||||
description: '清空组件数据'
|
||||
},
|
||||
{
|
||||
actionType: 'initDrag',
|
||||
actionLabel: '开启排序',
|
||||
description: '开启表格拖拽排序功能'
|
||||
}
|
||||
];
|
||||
|
||||
@ -1071,6 +1202,16 @@ export class TableControlPlugin extends BasePlugin {
|
||||
name: 'affixHeader',
|
||||
label: '是否固定表头',
|
||||
pipeIn: defaultValue(false)
|
||||
}),
|
||||
getSchemaTpl('switch', {
|
||||
name: 'showFooterAddBtn',
|
||||
label: '展示底部新增按钮',
|
||||
pipeIn: defaultValue(true)
|
||||
}),
|
||||
getSchemaTpl('switch', {
|
||||
name: 'showTableAddBtn',
|
||||
label: '展示操作列新增按钮',
|
||||
pipeIn: defaultValue(true)
|
||||
})
|
||||
]
|
||||
},
|
||||
@ -1080,6 +1221,10 @@ export class TableControlPlugin extends BasePlugin {
|
||||
getSchemaTpl('className', {
|
||||
name: 'rowClassName',
|
||||
label: '行样式'
|
||||
}),
|
||||
getSchemaTpl('className', {
|
||||
name: 'toolbarClassName',
|
||||
label: '工具栏'
|
||||
})
|
||||
]
|
||||
})
|
||||
|
@ -109,21 +109,26 @@ export class ListControlPlugin extends BasePlugin {
|
||||
}
|
||||
];
|
||||
|
||||
subEditorVariable: Array<{label: string; children: any}> = [
|
||||
{
|
||||
label: '当前选项',
|
||||
children: [
|
||||
{
|
||||
label: '选项名称',
|
||||
value: 'label'
|
||||
},
|
||||
{
|
||||
label: '选项值',
|
||||
value: 'value'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
getSubEditorVariable(schema: any): Array<{label: string; children: any}> {
|
||||
let labelField = schema?.labelField || 'label';
|
||||
let valueField = schema?.valueField || 'value';
|
||||
|
||||
return [
|
||||
{
|
||||
label: '当前选项',
|
||||
children: [
|
||||
{
|
||||
label: '选项名称',
|
||||
value: labelField
|
||||
},
|
||||
{
|
||||
label: '选项值',
|
||||
value: valueField
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
panelBodyCreator = (context: BaseEventContext) => {
|
||||
return formItemControl(
|
||||
@ -201,7 +206,7 @@ export class ListControlPlugin extends BasePlugin {
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: `\${${this.getDisplayField(value)}}`,
|
||||
tpl: `\${${this.getDisplayField(data)}}`,
|
||||
wrapperComponent: '',
|
||||
inline: true
|
||||
}
|
||||
@ -275,16 +280,7 @@ export class ListControlPlugin extends BasePlugin {
|
||||
}
|
||||
|
||||
getDisplayField(data: any) {
|
||||
if (
|
||||
data.source ||
|
||||
(data.map &&
|
||||
Array.isArray(data.map) &&
|
||||
data.map[0] &&
|
||||
Object.keys(data.map[0]).length > 1)
|
||||
) {
|
||||
return data.labelField ?? 'label';
|
||||
}
|
||||
return 'label';
|
||||
return data?.labelField ?? 'label';
|
||||
}
|
||||
|
||||
editDetail(id: string, field: string) {
|
||||
|
@ -50,7 +50,7 @@ export class PickerControlPlugin extends BasePlugin {
|
||||
value: 'B'
|
||||
}
|
||||
],
|
||||
modalClassName: 'app-popover'
|
||||
modalClassName: 'app-popover :AMISCSSWrapper'
|
||||
};
|
||||
previewSchema: any = {
|
||||
type: 'form',
|
||||
|
@ -1,4 +1,9 @@
|
||||
import {getI18nEnabled, registerEditorPlugin} from 'amis-editor-core';
|
||||
import {
|
||||
RendererPluginAction,
|
||||
RendererPluginEvent,
|
||||
getI18nEnabled,
|
||||
registerEditorPlugin
|
||||
} from 'amis-editor-core';
|
||||
import {
|
||||
ActiveEventContext,
|
||||
BaseEventContext,
|
||||
@ -8,6 +13,10 @@ import {
|
||||
} from 'amis-editor-core';
|
||||
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
|
||||
import {mockValue} from 'amis-editor-core';
|
||||
import {
|
||||
getArgsWrapper,
|
||||
getEventControlConfig
|
||||
} from '../renderer/event-control/helper';
|
||||
|
||||
export class ImagePlugin extends BasePlugin {
|
||||
static id = 'ImagePlugin';
|
||||
@ -34,6 +43,112 @@ export class ImagePlugin extends BasePlugin {
|
||||
value: mockValue({type: 'image'})
|
||||
};
|
||||
|
||||
// 事件定义
|
||||
events: RendererPluginEvent[] = [
|
||||
{
|
||||
eventName: 'click',
|
||||
eventLabel: '点击',
|
||||
description: '点击时触发',
|
||||
defaultShow: true,
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
context: {
|
||||
type: 'object',
|
||||
title: '上下文',
|
||||
properties: {
|
||||
nativeEvent: {
|
||||
type: 'object',
|
||||
title: '鼠标事件对象'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'mouseenter',
|
||||
eventLabel: '鼠标移入',
|
||||
description: '鼠标移入时触发',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
context: {
|
||||
type: 'object',
|
||||
title: '上下文',
|
||||
properties: {
|
||||
nativeEvent: {
|
||||
type: 'object',
|
||||
title: '鼠标事件对象'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'mouseleave',
|
||||
eventLabel: '鼠标移出',
|
||||
description: '鼠标移出时触发',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
context: {
|
||||
type: 'object',
|
||||
title: '上下文',
|
||||
properties: {
|
||||
nativeEvent: {
|
||||
type: 'object',
|
||||
title: '鼠标事件对象'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 动作定义
|
||||
actions: RendererPluginAction[] = [
|
||||
{
|
||||
actionType: 'preview',
|
||||
actionLabel: '预览',
|
||||
description: '预览图片'
|
||||
},
|
||||
{
|
||||
actionType: 'zoom',
|
||||
actionLabel: '调整图片比例',
|
||||
description: '将图片等比例放大或缩小',
|
||||
schema: {
|
||||
type: 'container',
|
||||
body: [
|
||||
getArgsWrapper([
|
||||
getSchemaTpl('formulaControl', {
|
||||
name: 'scale',
|
||||
mode: 'horizontal',
|
||||
variables: '${variables}',
|
||||
horizontal: {
|
||||
leftFixed: 4 // 需要设置下leftFixed,否则这个字段的控件没有与其他字段的控件左对齐
|
||||
},
|
||||
label: tipedLabel(
|
||||
'调整比例',
|
||||
'定义每次放大或缩小图片的百分比大小,正值为放大,负值为缩小,默认50'
|
||||
),
|
||||
value: 50,
|
||||
size: 'lg'
|
||||
})
|
||||
])
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
panelTitle = '图片';
|
||||
panelJustify = true;
|
||||
panelBodyCreator = (context: BaseEventContext) => {
|
||||
@ -63,7 +178,7 @@ export class ImagePlugin extends BasePlugin {
|
||||
pipeIn: defaultValue('thumb'),
|
||||
options: [
|
||||
{
|
||||
label: '缩率图',
|
||||
label: '缩略图',
|
||||
value: 'thumb'
|
||||
},
|
||||
{
|
||||
@ -130,6 +245,24 @@ export class ImagePlugin extends BasePlugin {
|
||||
getSchemaTpl('imageUrl', {
|
||||
name: 'defaultImage',
|
||||
label: tipedLabel('占位图', '无数据时显示的图片')
|
||||
}),
|
||||
getSchemaTpl('formulaControl', {
|
||||
name: 'maxScale',
|
||||
mode: 'horizontal',
|
||||
label: tipedLabel(
|
||||
'放大极限',
|
||||
'定义动作调整图片大小的最大百分比,默认200'
|
||||
),
|
||||
value: 200
|
||||
}),
|
||||
getSchemaTpl('formulaControl', {
|
||||
name: 'minScale',
|
||||
mode: 'horizontal',
|
||||
label: tipedLabel(
|
||||
'缩小极限',
|
||||
'定义动作调整图片大小的最小百分比,默认50'
|
||||
),
|
||||
value: 50
|
||||
})
|
||||
]
|
||||
},
|
||||
@ -245,6 +378,16 @@ export class ImagePlugin extends BasePlugin {
|
||||
},
|
||||
getSchemaTpl('theme:cssCode')
|
||||
])
|
||||
},
|
||||
{
|
||||
title: '事件',
|
||||
className: 'p-none',
|
||||
body: [
|
||||
getSchemaTpl('eventControl', {
|
||||
name: 'onEvent',
|
||||
...getEventControlConfig(this.manager, context)
|
||||
})
|
||||
]
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ export class ImagesPlugin extends BasePlugin {
|
||||
pluginIcon = 'images-plugin';
|
||||
scaffold = {
|
||||
type: 'images',
|
||||
imageGallaryClassName: 'app-popover'
|
||||
imageGallaryClassName: 'app-popover :AMISCSSWrapper'
|
||||
};
|
||||
previewSchema = {
|
||||
...this.scaffold,
|
||||
|
@ -29,6 +29,7 @@ export class NavPlugin extends BasePlugin {
|
||||
scaffold = {
|
||||
type: 'nav',
|
||||
stacked: true,
|
||||
popupClassName: 'app-popover :AMISCSSWrapper',
|
||||
links: [
|
||||
{
|
||||
label: '页面1',
|
||||
|
@ -62,9 +62,13 @@ export class PaginationPlugin extends BasePlugin {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
value: {
|
||||
type: 'string',
|
||||
page: {
|
||||
type: 'number',
|
||||
title: '当前页码值'
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
title: '每页显示的记录数'
|
||||
}
|
||||
},
|
||||
description: '当前数据域,可以通过.字段名读取对应的值'
|
||||
|
@ -321,6 +321,7 @@ export class SwitchContainerPlugin extends LayoutBasePlugin {
|
||||
name: 'items',
|
||||
label: '状态列表',
|
||||
addTip: '新增组件状态',
|
||||
minLength: 1,
|
||||
items: [
|
||||
{
|
||||
type: 'input-text',
|
||||
@ -356,6 +357,10 @@ export class SwitchContainerPlugin extends LayoutBasePlugin {
|
||||
title: '外观',
|
||||
className: 'p-none',
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
getSchemaTpl('theme:base', {
|
||||
collapsed: false,
|
||||
extra: []
|
||||
}),
|
||||
{
|
||||
title: '布局',
|
||||
body: [
|
||||
@ -460,7 +465,15 @@ export class SwitchContainerPlugin extends LayoutBasePlugin {
|
||||
getSchemaTpl('layout:stickyPosition')
|
||||
]
|
||||
},
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']})
|
||||
{
|
||||
title: '自定义样式',
|
||||
body: [
|
||||
{
|
||||
type: 'theme-cssCode',
|
||||
label: false
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
|
@ -349,6 +349,32 @@ export class TablePlugin extends BasePlugin {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'rowDbClick',
|
||||
eventLabel: '行双击',
|
||||
description: '双击整行事件',
|
||||
dataSchema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
title: '数据',
|
||||
properties: {
|
||||
item: {
|
||||
type: 'object',
|
||||
title: '当前行记录'
|
||||
},
|
||||
index: {
|
||||
type: 'number',
|
||||
title: '当前行索引'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
eventName: 'rowMouseEnter',
|
||||
eventLabel: '鼠标移入行事件',
|
||||
|
@ -7,7 +7,7 @@ import {findDOMNode} from 'react-dom';
|
||||
import cx from 'classnames';
|
||||
import get from 'lodash/get';
|
||||
import Sortable from 'sortablejs';
|
||||
import {FormItem, Button, Icon, render as amisRender} from 'amis';
|
||||
import {FormItem, Button, Icon, render as amisRender, toast} from 'amis';
|
||||
import {autobind} from 'amis-editor-core';
|
||||
import type {Option} from 'amis';
|
||||
import {createObject, FormControlProps} from 'amis-core';
|
||||
@ -30,7 +30,6 @@ export type SourceType = 'custom' | 'api' | 'apicenter' | 'variable';
|
||||
|
||||
export interface OptionControlState {
|
||||
items: Array<PlainObject>;
|
||||
api: SchemaApi;
|
||||
labelField: string;
|
||||
valueField: string;
|
||||
}
|
||||
@ -50,7 +49,6 @@ export default class ListItemControl extends React.Component<
|
||||
|
||||
this.state = {
|
||||
items: this.transformOptions(props),
|
||||
api: props.data.source,
|
||||
labelField: props.data.labelField || 'title',
|
||||
valueField: props.data.valueField
|
||||
};
|
||||
@ -173,6 +171,12 @@ export default class ListItemControl extends React.Component<
|
||||
*/
|
||||
handleDelete(index: number) {
|
||||
const items = this.state.items.concat();
|
||||
const minLength = this.props.minLength;
|
||||
|
||||
if (minLength > 0 && items.length <= minLength) {
|
||||
toast.warning(`列表项数目不能少于${minLength}`);
|
||||
return;
|
||||
}
|
||||
|
||||
items.splice(index, 1);
|
||||
this.setState({items}, () => this.onChange());
|
||||
|
@ -449,19 +449,29 @@ export class CRUDColumnControl extends React.Component<
|
||||
size="sm"
|
||||
className={cx('flex')}
|
||||
/>
|
||||
) : Array.isArray(options) && options.length > 0 ? (
|
||||
) : (
|
||||
<>
|
||||
{this.renderHeader()}
|
||||
<ul className={cx('ae-CRUDConfigControl-list')} ref={this.dragRef}>
|
||||
{options.map((item, index) => {
|
||||
return this.renderOption(item, index);
|
||||
})}
|
||||
</ul>
|
||||
{Array.isArray(options) && options.length > 0 ? (
|
||||
<ul
|
||||
className={cx('ae-CRUDConfigControl-list')}
|
||||
ref={this.dragRef}
|
||||
>
|
||||
{options.map((item, index) => {
|
||||
return this.renderOption(item, index);
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<ul
|
||||
className={cx('ae-CRUDConfigControl-list')}
|
||||
ref={this.dragRef}
|
||||
>
|
||||
<p className={cx(`ae-CRUDConfigControl-placeholder`)}>
|
||||
暂无数据
|
||||
</p>
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<ul className={cx('ae-CRUDConfigControl-list')} ref={this.dragRef}>
|
||||
<p className={cx(`ae-CRUDConfigControl-placeholder`)}>暂无数据</p>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{showAddModal ? (
|
||||
|
@ -1244,8 +1244,6 @@ export const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
'path',
|
||||
'value',
|
||||
'index',
|
||||
'fromPage',
|
||||
'fromApp',
|
||||
'__valueInput',
|
||||
'__comboType',
|
||||
'__containerType'
|
||||
@ -1283,29 +1281,34 @@ export const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
supportComponents: 'byComponent',
|
||||
schema: [
|
||||
{
|
||||
name: '__actionSubType',
|
||||
type: 'radios',
|
||||
label: '动作类型',
|
||||
mode: 'horizontal',
|
||||
options: [
|
||||
{label: '组件变量', value: 'cmpt'},
|
||||
{label: '页面变量', value: 'page'},
|
||||
{label: '内存变量', value: 'app'}
|
||||
],
|
||||
value:
|
||||
'${args.fromApp ? "app" : args.fromPage ? "page" : "cmpt"}',
|
||||
onChange: (value: string, oldVal: any, data: any, form: any) => {
|
||||
form.setValueByName('__valueInput', undefined);
|
||||
form.setValueByName('args.value', undefined);
|
||||
form.deleteValueByName('args.path');
|
||||
form.deleteValueByName('args.fromApp');
|
||||
form.deleteValueByName('args.fromPage');
|
||||
|
||||
if (value === 'page') {
|
||||
form.setValueByName('args.fromPage', true);
|
||||
} else if (value === 'app') {
|
||||
form.setValueByName('args.fromApp', true);
|
||||
}
|
||||
children: ({render, data}: any) => {
|
||||
const path = data?.args?.path || '';
|
||||
return render('setValueType', {
|
||||
name: '__actionSubType',
|
||||
type: 'radios',
|
||||
label: '动作类型',
|
||||
mode: 'horizontal',
|
||||
options: [
|
||||
{label: '组件变量', value: 'cmpt'},
|
||||
{label: '页面变量', value: 'page'},
|
||||
{label: '内存变量', value: 'app'}
|
||||
],
|
||||
value: /^appVariables/.test(path) // 只需要初始化时更新value
|
||||
? 'app'
|
||||
: /^(__page|__query)/.test(path)
|
||||
? 'page'
|
||||
: 'cmpt',
|
||||
onChange: (
|
||||
value: string,
|
||||
oldVal: any,
|
||||
data: any,
|
||||
form: any
|
||||
) => {
|
||||
form.setValueByName('__valueInput', undefined);
|
||||
form.setValueByName('args.value', undefined);
|
||||
form.deleteValueByName('args.path');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// 组件变量
|
||||
@ -2349,6 +2352,12 @@ export const COMMON_ACTION_SCHEMA_MAP: {
|
||||
},
|
||||
confirm: {
|
||||
descDetail: (info: any) => <div>打开确认对话框</div>
|
||||
},
|
||||
preview: {
|
||||
descDetail: (info: any) => <div>预览图片</div>
|
||||
},
|
||||
zoom: {
|
||||
descDetail: (info: any) => <div>调整图片比例</div>
|
||||
}
|
||||
};
|
||||
|
||||
@ -3212,7 +3221,7 @@ export const getEventControlConfig = (
|
||||
showCloseButton: true,
|
||||
showErrorMsg: true,
|
||||
showLoading: true,
|
||||
className: 'app-popover',
|
||||
className: 'app-popover :AMISCSSWrapper',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
@ -3239,7 +3248,7 @@ export const getEventControlConfig = (
|
||||
inline: false
|
||||
}
|
||||
],
|
||||
className: 'app-popover',
|
||||
className: 'app-popover :AMISCSSWrapper',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
@ -3376,9 +3385,7 @@ export const getEventControlConfig = (
|
||||
/** 应用变量赋值 */
|
||||
action.args = {
|
||||
path: config.args.path,
|
||||
value: config.args?.value ?? '',
|
||||
fromPage: action.args?.fromPage,
|
||||
fromApp: action.args?.fromApp
|
||||
value: config.args?.value ?? ''
|
||||
};
|
||||
|
||||
action.hasOwnProperty('componentId') && delete action.componentId;
|
||||
|
@ -148,7 +148,7 @@ export class TextareaFormulaControl extends React.Component<
|
||||
constructor(props: TextareaFormulaControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: '',
|
||||
value: this.props.value || '',
|
||||
variables: [],
|
||||
formulaPickerOpen: false,
|
||||
formulaPickerValue: '',
|
||||
|
@ -563,6 +563,18 @@ test('evalute:Math', () => {
|
||||
expect(evaluate('${POW(2, infinity)}', data)).toBe(data.infinity);
|
||||
});
|
||||
|
||||
test('evalute:UUID', () => {
|
||||
function isUUIDv4(value: string) {
|
||||
return /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.test(
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
expect(isUUIDv4(evaluate('${UUID()}', {}))).toBe(true);
|
||||
expect(evaluate('${UUID()}', {}).length).toBe(36);
|
||||
expect(evaluate('${UUID(8)}', {}).length).toBe(8);
|
||||
});
|
||||
|
||||
test('evalute:namespace', () => {
|
||||
localStorage.setItem('a', '1');
|
||||
localStorage.setItem('b', '2');
|
||||
|
@ -591,6 +591,16 @@
|
||||
|
||||
返回:a.json`。
|
||||
|
||||
### UUID
|
||||
|
||||
用法:`UUID(8)`
|
||||
|
||||
* `length:number` 生成的UUID字符串长度,默认为32位
|
||||
|
||||
返回:`string` 生成的UUID字符串
|
||||
|
||||
生成UUID字符串
|
||||
|
||||
## 日期函数
|
||||
|
||||
### DATE
|
||||
|
@ -1022,6 +1022,23 @@ export const doc: {
|
||||
},
|
||||
namespace: '文本函数'
|
||||
},
|
||||
{
|
||||
name: 'UUID',
|
||||
description: '生成UUID字符串',
|
||||
example: 'UUID(8)',
|
||||
params: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'length',
|
||||
description: '生成的UUID字符串长度,默认为32位'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'string',
|
||||
description: '生成的UUID字符串'
|
||||
},
|
||||
namespace: '文本函数'
|
||||
},
|
||||
{
|
||||
name: 'DATE',
|
||||
description:
|
||||
|
@ -1514,6 +1514,21 @@ export class Evaluator {
|
||||
return text.split(/[\\/]/).pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成UUID字符串
|
||||
*
|
||||
* @param {number} length - 生成的UUID字符串长度,默认为32位
|
||||
* @example UUID()
|
||||
* @example UUID(8)
|
||||
* @namespace 文本函数
|
||||
*
|
||||
* @returns {string} 生成的UUID字符串
|
||||
*/
|
||||
fnUUID(length: number = 36) {
|
||||
const len = Math.min(Math.max(length, 0), 36);
|
||||
return uuidv4().slice(0, len);
|
||||
}
|
||||
|
||||
// 日期函数
|
||||
|
||||
/**
|
||||
@ -2414,3 +2429,25 @@ export function createObject(
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function createStr() {
|
||||
return (
|
||||
'00000000000000000' + (Math.random() * 0xffffffffffffffff).toString(16)
|
||||
).slice(-16);
|
||||
}
|
||||
|
||||
export function uuidv4() {
|
||||
const a = createStr();
|
||||
const b = createStr();
|
||||
return (
|
||||
a.slice(0, 8) +
|
||||
'-' +
|
||||
a.slice(8, 12) +
|
||||
'-4' +
|
||||
a.slice(13) +
|
||||
'-a' +
|
||||
b.slice(1, 4) +
|
||||
'-' +
|
||||
b.slice(4)
|
||||
);
|
||||
}
|
||||
|
@ -786,6 +786,7 @@
|
||||
--transfer-base-header-paddingBottom: var(--sizes-size-5);
|
||||
--transfer-base-header-paddingLeft: var(--sizes-size-8);
|
||||
--transfer-base-header-paddingRight: var(--sizes-size-8);
|
||||
--transfer-base-footer-border-color: var(--colors-neutral-line-8);
|
||||
--transfer-base-body-paddingTop: var(--sizes-size-0);
|
||||
--transfer-base-body-paddingBottom: var(--sizes-size-0);
|
||||
--transfer-base-body-paddingLeft: var(--sizes-size-0);
|
||||
@ -2052,6 +2053,7 @@
|
||||
--Tabs-onActive-bg: var(--background);
|
||||
--Tabs-onActive-borderColor: var(--borderColor);
|
||||
--Tabs-onActive-color: var(--colors-neutral-text-2);
|
||||
--Tabs-onError-color: var(--colors-error-5);
|
||||
--Tabs-onDisabled-color: var(--colors-neutral-text-7);
|
||||
--Tabs-onHover-borderColor: var(--colors-neutral-line-8);
|
||||
--Tabs-add-icon-size: #{px2rem(15px)};
|
||||
@ -4130,6 +4132,7 @@
|
||||
var(--combo-vertical-right-border-color)
|
||||
var(--combo-vertical-bottom-border-color)
|
||||
var(--combo-vertical-left-border-color);
|
||||
--Combo--vertical-item--onError-borderColor: var(--colors-error-5);
|
||||
--Combo--vertical-item-borderRadius: var(
|
||||
--combo-vertical-top-left-border-radius
|
||||
)
|
||||
|
@ -274,22 +274,18 @@ $Table-strip-bg: transparent;
|
||||
--DropDown-menu-borderColor: var(--borderColor);
|
||||
--DropDown-menu-borderRadius: var(--borderRadius);
|
||||
--DropDown-menu-borderWidth: var(--borderWidth);
|
||||
--DropDown-menu-boxShadow: var(--shadows-shadow-normal);
|
||||
--DropDown-menu-height: #{px2rem(34px)};
|
||||
--DropDown-menu-boxShadow: var(--Form-select-outer-boxShadow);
|
||||
--DropDown-menu-height: #{px2rem(32px)};
|
||||
--DropDown-menu-minWidth: #{px2rem(160px)};
|
||||
--DropDown-menu-paddingX: 0;
|
||||
--DropDown-menu-paddingY: var(--gap-xs);
|
||||
--DropDown-menuItem-onHover-bg: var(--ListMenu-item--onHover-bg);
|
||||
--DropDown-menuItem-onHover-bg: var(--Form-select-menu-onHover-bg);
|
||||
--DropDown-group-color: #848b99;
|
||||
--DropDown-menuItem-color: #151a26;
|
||||
--DropDown-menuItem-onHover-color: var(--colors-brand-5);
|
||||
--DropDown-menuItem-onHover-color: var(--Form-select-menu-onHover-color);
|
||||
--DropDown-menuItem-onActive-color: var(--colors-brand-5);
|
||||
--DropDown-menuItem-onDisabled-color: #b4b6ba;
|
||||
--DropDown-menuItem-paddingX: var(--gap-sm);
|
||||
--DropDown-menuItem-paddingY: calc(
|
||||
(var(--DropDown-menu-height) - var(--fontSizeBase) * var(--lineHeightBase)) /
|
||||
2
|
||||
);
|
||||
--DropDown-menuItem-paddingX: var(--select-base-default-option-paddingRight);
|
||||
|
||||
--Fieldset-legend-bgColor: var(--colors-neutral-fill-11);
|
||||
|
||||
|
@ -57,6 +57,7 @@
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: px2rem(300px);
|
||||
margin-top: px2rem(4px);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,10 +68,11 @@
|
||||
|
||||
&-menuItem,
|
||||
&-menu > li {
|
||||
padding: var(--DropDown-menuItem-paddingY) var(--DropDown-menuItem-paddingX);
|
||||
padding: 0 var(--DropDown-menuItem-paddingX);
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
height: var(--DropDown-menu-height);
|
||||
line-height: var(--DropDown-menu-height);
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
color: var(--DropDown-menuItem-color);
|
||||
@ -95,7 +97,7 @@
|
||||
|
||||
&.#{$ns}DropDown-divider {
|
||||
height: px2rem(1px);
|
||||
margin: px2rem(9px) 0;
|
||||
margin: px2rem(4px) 0;
|
||||
overflow: hidden;
|
||||
background: var(--DropDown-menu-borderColor);
|
||||
padding: 0;
|
||||
|
@ -519,14 +519,13 @@
|
||||
.#{$ns}Nav-Menu-submenu-arrow {
|
||||
display: inline-block;
|
||||
font-size: px2rem(9px);
|
||||
vertical-align: middle;
|
||||
text-transform: none;
|
||||
text-rendering: auto;
|
||||
line-height: px2rem(20px);
|
||||
margin-left: px2rem(5px);
|
||||
|
||||
& > svg {
|
||||
top: auto;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@
|
||||
}
|
||||
}
|
||||
&-simple {
|
||||
>ul >li {
|
||||
> ul > li {
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: none;
|
||||
@ -94,8 +94,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&-next {
|
||||
> span {
|
||||
cursor: pointer;
|
||||
@ -113,47 +111,47 @@
|
||||
align-items: center;
|
||||
height: var(--Pagination-height);
|
||||
|
||||
&-left {
|
||||
color: var(--Pagination-light-color);
|
||||
}
|
||||
input {
|
||||
min-width: px2rem(50px);
|
||||
width: px2rem(50px);
|
||||
height: var(--Pagination-height);
|
||||
line-height: var(--Pagination-height);
|
||||
// height: var(--Pagination-height);
|
||||
border: none;
|
||||
border: var(--borderWidth) solid var(--borderColor);
|
||||
border-radius: var(--borderRadius) 0 0 var(--borderRadius);
|
||||
padding: var(--Pagination-padding);
|
||||
margin-left: px2rem(8px);
|
||||
text-align: center;
|
||||
&-left {
|
||||
color: var(--Pagination-light-color);
|
||||
}
|
||||
input {
|
||||
min-width: px2rem(50px);
|
||||
width: px2rem(50px);
|
||||
height: var(--Pagination-height);
|
||||
line-height: var(--Pagination-height);
|
||||
// height: var(--Pagination-height);
|
||||
border: none;
|
||||
border: var(--borderWidth) solid var(--borderColor);
|
||||
border-radius: var(--borderRadius) 0 0 var(--borderRadius);
|
||||
padding: var(--Pagination-padding);
|
||||
margin-left: px2rem(8px);
|
||||
text-align: center;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
outline: none;
|
||||
// border: var(--borderWidth) solid var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
&:focus,
|
||||
&:hover {
|
||||
outline: none;
|
||||
// border: var(--borderWidth) solid var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
&-right {
|
||||
display: inline-block;
|
||||
width: px2rem(32px);
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
height: var(--Pagination-height);
|
||||
line-height: var(--Pagination-height);
|
||||
border: var(--borderWidth) solid var(--borderColor);
|
||||
border-left: none;
|
||||
border-radius: 0 var(--borderRadius) var(--borderRadius) 0;
|
||||
font-size: var(--fontSizeSm);
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
border-left: var(--borderWidth) solid var(--primary);
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
&-right {
|
||||
display: inline-block;
|
||||
width: px2rem(32px);
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
height: var(--Pagination-height);
|
||||
line-height: var(--Pagination-height);
|
||||
border: var(--borderWidth) solid var(--borderColor);
|
||||
border-left: none;
|
||||
border-radius: 0 var(--borderRadius) var(--borderRadius) 0;
|
||||
font-size: var(--fontSizeSm);
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
border-left: var(--borderWidth) solid var(--primary);
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +159,6 @@
|
||||
line-height: px2rem(30px);
|
||||
|
||||
.#{$ns}Pagination-item {
|
||||
margin-left: px2rem(8px);
|
||||
&:nth-child(1) {
|
||||
margin-left: 0;
|
||||
}
|
||||
@ -198,4 +195,4 @@
|
||||
}
|
||||
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
@ -1108,3 +1108,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}AutoFilterToolbar {
|
||||
display: block;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
|
||||
> .#{$ns}Button {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
.#{$ns}Tabs-addable {
|
||||
display: flex;
|
||||
margin-left: var(--Tabs-add-margin);
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: var(--Tabs--line-addPadding);
|
||||
margin-left: var(--Tabs-add-margin);
|
||||
margin-bottom: px2rem(3px);
|
||||
padding-bottom: px2rem(8px);
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
|
||||
@ -60,16 +60,19 @@
|
||||
// }
|
||||
|
||||
&-arrow {
|
||||
margin: var(--Tabs--line-addPadding);
|
||||
width: 16px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
box-sizing: content-box;
|
||||
margin-bottom: px2rem(3px);
|
||||
padding-bottom: px2rem(8px);
|
||||
|
||||
.iconfont {
|
||||
font-size: var(--Remark-icon-fontSize);
|
||||
.icon {
|
||||
top: 0;
|
||||
width: var(--Remark-icon-fontSize);
|
||||
height: var(--Remark-icon-fontSize);
|
||||
}
|
||||
&:hover {
|
||||
color: var(--icon-onHover-color);
|
||||
@ -77,6 +80,9 @@
|
||||
|
||||
&--left {
|
||||
padding-right: 16px;
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&--right {
|
||||
@ -242,6 +248,10 @@
|
||||
border-color: var(--Tabs-onActive-borderColor);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
&.has-error > a:first-child {
|
||||
color: var(--Tabs-onError-color) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -672,6 +682,7 @@
|
||||
.#{$ns}Tabs-addable {
|
||||
padding: 0 var(--Tabs--tiled-add-gap);
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
white-space: nowrap;
|
||||
border-style: solid;
|
||||
border-color: var(--Tabs-borderColor);
|
||||
@ -1112,6 +1123,7 @@
|
||||
& > .#{$ns}Tabs-linksContainer {
|
||||
> .#{$ns}Tabs-linksContainer-arrow {
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .#{$ns}Tabs-linksContainer-main > .#{$ns}Tabs-links {
|
||||
@ -1224,21 +1236,6 @@
|
||||
> .#{$ns}Tabs-linksContainer {
|
||||
margin-bottom: calc(var(--Tabs-borderWidth) * -1);
|
||||
|
||||
&.#{$ns}Tabs-linksContainer--overflow
|
||||
> .#{$ns}Tabs-linksContainer-main
|
||||
> .#{$ns}Tabs-links
|
||||
> .#{$ns}Tabs-link {
|
||||
&:first-of-type {
|
||||
border-left-width: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-right-width: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Tabs-linksContainer-arrow {
|
||||
width: var(--Tabs--strong-arrow-size);
|
||||
margin-bottom: 0;
|
||||
@ -1250,14 +1247,14 @@
|
||||
|
||||
&--left {
|
||||
padding-right: 0;
|
||||
border-right-width: 0;
|
||||
border-top-left-radius: var(--Tabs-borderRadius);
|
||||
margin-right: px2rem(8px);
|
||||
}
|
||||
|
||||
&--right {
|
||||
padding-left: 0;
|
||||
border-left-width: 0;
|
||||
border-top-right-radius: var(--Tabs-borderRadius);
|
||||
border-top-left-radius: var(--Tabs-borderRadius);
|
||||
margin-left: px2rem(8px);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,6 +258,12 @@
|
||||
var(--combo-vertical-paddingRight) var(--combo-vertical-paddingBottom)
|
||||
var(--combo-vertical-paddingLeft);
|
||||
position: relative;
|
||||
|
||||
&.has-error {
|
||||
border-color: var(
|
||||
--Combo--vertical-item--onError-borderColor
|
||||
) !important; // 因为下面的规则权重更高 &:not(.is-disabled) > .#{$ns}Combo-items > .#{$ns}Combo-item:hover
|
||||
}
|
||||
}
|
||||
|
||||
> .#{$ns}Combo-items > .#{$ns}Combo-item {
|
||||
|
@ -257,12 +257,9 @@
|
||||
}
|
||||
|
||||
.#{$ns}DateRangePicker-popover {
|
||||
margin: px2rem(2px) 0 0;
|
||||
|
||||
&.#{$ns}PopOver--leftTopLeftBottom,
|
||||
&.#{$ns}PopOver--rightTopRightBottom {
|
||||
margin: px2rem(-2px) 0 0;
|
||||
}
|
||||
border: var(--Form-select-outer-borderWidth) solid
|
||||
var(--Form-input-onFocused-borderColor);
|
||||
box-shadow: var(--Form-select-outer-boxShadow);
|
||||
}
|
||||
|
||||
.#{$ns}DateRangePicker-popup {
|
||||
|
@ -200,11 +200,9 @@
|
||||
|
||||
.#{$ns}DatePicker-popover {
|
||||
margin: px2rem(2px) 0 0;
|
||||
|
||||
&.#{$ns}PopOver--leftTopLeftBottom,
|
||||
&.#{$ns}PopOver--rightTopRightBottom {
|
||||
margin: px2rem(-2px) 0 0;
|
||||
}
|
||||
border: var(--Form-select-outer-borderWidth) solid
|
||||
var(--Form-input-onFocused-borderColor);
|
||||
box-shadow: var(--Form-select-outer-boxShadow);
|
||||
}
|
||||
|
||||
// 移动端输入框样式
|
||||
|
@ -132,6 +132,13 @@
|
||||
max-width: 100%;
|
||||
margin-top: -1px;
|
||||
border-color: var(--Form-input-onFocused-borderColor);
|
||||
|
||||
&.#{$ns}PopOver--v-top {
|
||||
margin-top: px2rem(4px);
|
||||
}
|
||||
&.#{$ns}PopOver--v-bottom {
|
||||
margin-bottom: px2rem(4px);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}MapPicker {
|
||||
|
@ -4,7 +4,7 @@
|
||||
.#{$ns}NestedSelect-menu {
|
||||
padding-top: px2rem(4px);
|
||||
padding-bottom: px2rem(4px);
|
||||
box-shadow: 0 px2rem(2px) px2rem(8px) 0 rgba(7, 12, 20, 0.12);
|
||||
box-shadow: var(--Form-select-outer-boxShadow);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,8 +18,6 @@
|
||||
|
||||
&-optionArrowRight {
|
||||
display: inline-block;
|
||||
padding-right: var(--Form-select-icon-rigin);
|
||||
|
||||
svg {
|
||||
width: px2rem(10px);
|
||||
height: px2rem(10px);
|
||||
@ -29,6 +27,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-optionArrowRight.is-disabled {
|
||||
svg {
|
||||
color: var(--text--muted-color);
|
||||
}
|
||||
}
|
||||
|
||||
&-menuOuter {
|
||||
display: flex;
|
||||
}
|
||||
@ -56,19 +60,23 @@
|
||||
max-height: px2rem(175px);
|
||||
background: var(--Form-select-menu-bg);
|
||||
color: var(--Form-select-menu-color);
|
||||
border: var(--Form-select-outer-borderWidth) solid
|
||||
var(--Form-input-onFocused-borderColor);
|
||||
border-radius: var(--borderRadius);
|
||||
box-shadow: var(--Form-select-outer-boxShadow);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
border: var(--Form-select-outer-borderWidth) solid
|
||||
var(--Form-input-onFocused-borderColor);
|
||||
&:not(:first-child) {
|
||||
border-left: 0;
|
||||
margin-left: px2rem(4px);
|
||||
}
|
||||
|
||||
.#{$ns}NestedSelect-option {
|
||||
position: relative;
|
||||
padding-left: var(--gap-md);
|
||||
padding: var(--select-base-default-option-paddingTop)
|
||||
var(--select-base-default-option-paddingRight)
|
||||
var(--select-base-default-option-paddingBottom)
|
||||
var(--select-base-default-option-paddingLeft);
|
||||
min-height: var(--select-base-default-option-line-height);
|
||||
line-height: var(--select-base-default-option-line-height);
|
||||
cursor: pointer;
|
||||
@ -122,3 +130,8 @@
|
||||
height: px2rem(340px);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}NestedSelect-popover {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
@ -584,26 +584,15 @@
|
||||
}
|
||||
|
||||
.#{$ns}Select-popover {
|
||||
margin-top: calc(var(--Form-select-outer-borderWidth) * -1);
|
||||
|
||||
background: var(--Form-select-menu-bg);
|
||||
color: var(--Form-select-menu-color);
|
||||
border: var(--Form-select-outer-borderWidth) solid
|
||||
var(--Form-input-onFocused-borderColor);
|
||||
box-shadow: var(--Form-select-outer-boxShadow);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
// min-width: px2rem(100px);
|
||||
|
||||
// PopOver 上已经配置了,这个要是配置就会覆盖,所以先干掉好了
|
||||
// z-index: 10;
|
||||
|
||||
&.#{$ns}PopOver--leftTopLeftBottom {
|
||||
margin-top: calc(
|
||||
(var(--Form-select-popoverGap) - var(--Form-select-outer-borderWidth)) *
|
||||
-1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}SelectControl {
|
||||
|
@ -319,7 +319,10 @@
|
||||
background: var(--Form-select-menu-bg);
|
||||
color: var(--Form-select-menu-color);
|
||||
border-radius: px2rem(2px);
|
||||
box-shadow: var(--menu-box-shadow);
|
||||
box-shadow: var(--Form-select-outer-boxShadow);
|
||||
border: var(--Form-select-outer-borderWidth) solid
|
||||
var(--Form-input-onFocused-borderColor);
|
||||
padding: px2rem(4px) 0;
|
||||
}
|
||||
|
||||
&-sugs {
|
||||
@ -328,13 +331,11 @@
|
||||
}
|
||||
|
||||
&-sugItem {
|
||||
padding: calc(
|
||||
(
|
||||
var(--Form-selectOption-height) - var(--Form-input-lineHeight) *
|
||||
var(--Form-input-fontSize) - #{px2rem(2px)}
|
||||
) / 2
|
||||
)
|
||||
px2rem(12px);
|
||||
padding: var(--select-base-default-option-paddingTop)
|
||||
var(--select-base-default-option-paddingRight)
|
||||
var(--select-base-default-option-paddingBottom)
|
||||
var(--select-base-default-option-paddingLeft);
|
||||
line-height: var(--select-base-default-option-line-height);
|
||||
|
||||
svg {
|
||||
width: px2rem(16px);
|
||||
|
@ -39,6 +39,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: 1px solid var(--transfer-base-footer-border-color);
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-end;
|
||||
padding: var(--gap-sm);
|
||||
|
||||
/* 底部空间较小,让Pagination紧凑一些 */
|
||||
&-pagination {
|
||||
& > ul {
|
||||
&.#{$ns}Pagination-item {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
& > li {
|
||||
--Pagination-minWidth: #{px2rem(22px)};
|
||||
--Pagination-height: #{px2rem(22px)};
|
||||
--Pagination-padding: 0 #{px2rem(6px)};
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Pagination-perpage {
|
||||
--select-base-default-paddingTop: 0;
|
||||
--select-base-default-paddingBottom: 0;
|
||||
--select-base-default-paddingLeft: #{px2rem(6px)};
|
||||
--select-base-default-paddingRight: #{px2rem(6px)};
|
||||
|
||||
margin-left: 0;
|
||||
|
||||
.#{$ns}Select-valueWrap {
|
||||
line-height: #{px2rem(22px)};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-select,
|
||||
&-result {
|
||||
overflow: hidden;
|
||||
@ -64,6 +100,10 @@
|
||||
var(--transfer-base-top-right-border-radius)
|
||||
var(--transfer-base-bottom-right-border-radius)
|
||||
var(--transfer-base-bottom-left-border-radius);
|
||||
|
||||
&--pagination {
|
||||
max-height: px2rem(475px);
|
||||
}
|
||||
}
|
||||
|
||||
&-select > &-selection,
|
||||
|
@ -55,6 +55,17 @@ export default class Markdown extends React.Component<MarkdownProps> {
|
||||
async _render() {
|
||||
const {content, options} = this.props;
|
||||
this.dom.innerHTML = markdown(content, options);
|
||||
|
||||
// @ts-ignore 需要用户手动加载 katex
|
||||
if (typeof renderMathInElement === 'function') {
|
||||
// @ts-ignore
|
||||
renderMathInElement(this.dom, {
|
||||
delimiters: [
|
||||
{left: '$$', right: '$$', display: true},
|
||||
{left: '$', right: '$', display: false}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -94,7 +94,6 @@ export interface BasicPaginationProps {
|
||||
popOverContainerSelector?: string;
|
||||
|
||||
onPageChange?: (page: number, perPage?: number, dir?: string) => void;
|
||||
dispatchEvent?: Function;
|
||||
}
|
||||
export interface PaginationProps
|
||||
extends BasicPaginationProps,
|
||||
@ -141,20 +140,13 @@ export class Pagination extends React.Component<
|
||||
}
|
||||
|
||||
async handlePageNumChange(page: number, perPage?: number, dir?: string) {
|
||||
const {disabled, onPageChange, dispatchEvent} = this.props;
|
||||
const {disabled, onPageChange} = this.props;
|
||||
const _page = isNaN(Number(page)) || Number(page) < 1 ? 1 : page;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rendererEvent = await dispatchEvent?.(
|
||||
'change',
|
||||
resolveEventData(this.props, {_page})
|
||||
);
|
||||
if (rendererEvent?.prevented) {
|
||||
return;
|
||||
}
|
||||
onPageChange?.(_page, perPage, dir);
|
||||
}
|
||||
|
||||
|
@ -383,11 +383,16 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
getStepValue(value: number, step: number) {
|
||||
const surplus = value % step;
|
||||
let result = 0;
|
||||
let closeNum = Math.floor(value - (value % step));
|
||||
// 余数 >= 步长一半 -> 向上取
|
||||
// 余数 < 步长一半 -> 向下取
|
||||
const _value = surplus >= step / 2 ? value : safeSub(value, step);
|
||||
while (result <= _value) {
|
||||
result = safeAdd(result, step);
|
||||
if (step < 1 || result === 0 || result === closeNum) {
|
||||
result = safeAdd(result, step);
|
||||
} else {
|
||||
result = closeNum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -502,7 +507,7 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
// 遍历刻度标记masks 寻找距离当前节点最近的刻度标记 并记录差值
|
||||
keys(marks).forEach((mKey: keyof MarksType) => {
|
||||
const mNum = isString(mKey) ? parseInt(mKey, 10) : mKey;
|
||||
if (mKey !== value) {
|
||||
if (mKey !== value && !isNaN(mNum)) {
|
||||
maxWidth = Math.min(Math.abs(curNum - mNum), maxWidth);
|
||||
}
|
||||
});
|
||||
|
@ -50,6 +50,7 @@ export interface TabProps extends ThemeProps {
|
||||
tip?: string;
|
||||
tab?: Schema;
|
||||
className?: string;
|
||||
tabClassName?: string;
|
||||
activeKey?: string | number;
|
||||
reload?: boolean;
|
||||
mountOnEnter?: boolean;
|
||||
@ -738,7 +739,7 @@ export class Tabs extends React.Component<TabsProps, any> {
|
||||
disabled && 'Tabs-linksContainer-arrow--disabled'
|
||||
)}
|
||||
>
|
||||
<i className={'iconfont icon-arrow-' + type} />
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import includes from 'lodash/includes';
|
||||
import debounce from 'lodash/debounce';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import unionWith from 'lodash/unionWith';
|
||||
|
||||
import {ThemeProps, themeable, findTree, differenceFromAll} from 'amis-core';
|
||||
import {BaseSelectionProps, BaseSelection, ItemRenderStates} from './Selection';
|
||||
import {Options, Option} from './Select';
|
||||
@ -24,6 +23,7 @@ import {ItemRenderStates as ResultItemRenderStates} from './ResultList';
|
||||
import ResultTableList from './ResultTableList';
|
||||
import ResultTreeList from './ResultTreeList';
|
||||
import {SpinnerExtraProps} from './Spinner';
|
||||
import Pagination from './Pagination';
|
||||
|
||||
export type SelectMode =
|
||||
| 'table'
|
||||
@ -113,6 +113,44 @@ export interface TransferProps
|
||||
checkAllLabel?: string;
|
||||
/** 树形模式下,给 tree 的属性 */
|
||||
onlyChildren?: boolean;
|
||||
/** 分页模式下累积的选项值,用于右侧回显 */
|
||||
accumulatedOptions?: Option[];
|
||||
/** 分页配置 */
|
||||
pagination?: {
|
||||
/** 是否开启分页 */
|
||||
enable: boolean;
|
||||
/** 分页组件CSS类名 */
|
||||
className?: string;
|
||||
/**
|
||||
* 通过控制layout属性的顺序,调整分页结构 total,perPage,pager,go
|
||||
* @default 'pager'
|
||||
*/
|
||||
layout?: string | Array<string>;
|
||||
|
||||
/**
|
||||
* 指定每页可以显示多少条
|
||||
* @default [10, 20, 50, 100]
|
||||
*/
|
||||
perPageAvailable?: Array<number>;
|
||||
|
||||
/**
|
||||
* 最多显示多少个分页按钮。
|
||||
*
|
||||
* @default 5
|
||||
*/
|
||||
maxButtons?: number;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
total?: number;
|
||||
popOverContainer?: any;
|
||||
popOverContainerSelector?: string;
|
||||
};
|
||||
/** 切换分页事件 */
|
||||
onPageChange?: (
|
||||
page: number,
|
||||
perPage?: number,
|
||||
direction?: 'forward' | 'backward'
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface TransferState {
|
||||
@ -549,10 +587,33 @@ export class Transfer<
|
||||
{this.state.searchResult !== null
|
||||
? this.renderSearchResult(props)
|
||||
: this.renderOptions(props)}
|
||||
|
||||
{this.renderFooter()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
const {classnames: cx, pagination, onPageChange} = this.props;
|
||||
|
||||
return pagination?.enable ? (
|
||||
<div className={cx('Transfer-footer')}>
|
||||
<Pagination
|
||||
className={cx('Transfer-footer-pagination', pagination.className)}
|
||||
activePage={pagination.page}
|
||||
perPage={pagination.perPage}
|
||||
total={pagination.total}
|
||||
layout={pagination.layout}
|
||||
maxButtons={pagination.maxButtons}
|
||||
perPageAvailable={pagination.perPageAvailable}
|
||||
popOverContainer={pagination.popOverContainer}
|
||||
popOverContainerSelector={pagination.popOverContainerSelector}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
||||
renderSearchResult(props: TransferProps) {
|
||||
const {
|
||||
searchResultMode,
|
||||
@ -827,9 +888,10 @@ export class Transfer<
|
||||
virtualThreshold,
|
||||
itemHeight,
|
||||
loadingConfig,
|
||||
showInvalidMatch
|
||||
showInvalidMatch,
|
||||
pagination,
|
||||
accumulatedOptions
|
||||
} = this.props;
|
||||
|
||||
const {resultSelectMode, isTreeDeferLoad} = this.state;
|
||||
const searchable = !isTreeDeferLoad && resultSearchable;
|
||||
|
||||
@ -840,7 +902,7 @@ export class Transfer<
|
||||
ref={this.domResultRef}
|
||||
classnames={cx}
|
||||
columns={columns!}
|
||||
options={options || []}
|
||||
options={(pagination?.enable ? accumulatedOptions : options) || []}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
option2value={option2value}
|
||||
@ -862,7 +924,7 @@ export class Transfer<
|
||||
loadingConfig={loadingConfig}
|
||||
classnames={cx}
|
||||
className={cx('Transfer-value')}
|
||||
options={options}
|
||||
options={(pagination?.enable ? accumulatedOptions : options) || []}
|
||||
valueField={'value'}
|
||||
value={value || []}
|
||||
onChange={onChange!}
|
||||
@ -915,7 +977,8 @@ export class Transfer<
|
||||
selectMode = 'list',
|
||||
translate: __,
|
||||
valueField = 'value',
|
||||
mobileUI
|
||||
mobileUI,
|
||||
pagination
|
||||
} = this.props as any;
|
||||
const {searchResult} = this.state;
|
||||
|
||||
@ -939,7 +1002,11 @@ export class Transfer<
|
||||
<div
|
||||
className={cx('Transfer', className, inline ? 'Transfer--inline' : '')}
|
||||
>
|
||||
<div className={cx('Transfer-select')}>
|
||||
<div
|
||||
className={cx('Transfer-select', {
|
||||
'Transfer-select--pagination': !!pagination?.enable
|
||||
})}
|
||||
>
|
||||
{this.renderSelect(this.props)}
|
||||
</div>
|
||||
<div className={cx('Transfer-mid', {'is-mobile': mobileUI})}>
|
||||
@ -949,7 +1016,12 @@ export class Transfer<
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={cx('Transfer-result', {'is-mobile': mobileUI})}>
|
||||
<div
|
||||
className={cx('Transfer-result', {
|
||||
'is-mobile': mobileUI,
|
||||
'Transfer-select--pagination': !!pagination?.enable
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
'Transfer-title',
|
||||
|
@ -20,6 +20,7 @@
|
||||
* 17. api 返回格式支持取对象中的第一个数组
|
||||
* 18. CRUD 事件
|
||||
* 19. fetchInitData silent 静默请求
|
||||
* 20. CRUD表头查询字段更新后严格比较场景
|
||||
*/
|
||||
|
||||
import {
|
||||
@ -1046,9 +1047,8 @@ test('17. should use the first array item in the response if provided', async ()
|
||||
)
|
||||
);
|
||||
|
||||
waitFor(() => {
|
||||
expect(container.querySelectorAll('tbody>tr').length).toBe(2);
|
||||
});
|
||||
await wait(200);
|
||||
expect(container.querySelectorAll('tbody>tr').length).toBe(2);
|
||||
});
|
||||
|
||||
describe('18. inner events', () => {
|
||||
@ -1149,3 +1149,169 @@ test('19. fetchInitData silent true', async () => {
|
||||
expect(notify).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
test('20. CRUD filters contain fields that modification inspection should use strict mode', async () => {
|
||||
let keyword;
|
||||
const mockFetcher = jest.fn().mockImplementation((req) => {
|
||||
/** mock.calls[0][0]拿不到filter里的参数,先用闭包测试吧 */
|
||||
keyword = req.data.version;
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
count: 0,
|
||||
items: []
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
"type": "crud",
|
||||
"name": "crud",
|
||||
"syncLocation": false,
|
||||
"api": {
|
||||
"method": "post",
|
||||
"url": "/api/mock/crud"
|
||||
},
|
||||
"filter": {
|
||||
"body": [
|
||||
{
|
||||
"type": "select",
|
||||
"name": "version",
|
||||
"label": "version",
|
||||
"clearable": true,
|
||||
"options": [
|
||||
{"label": "0", "value": 0},
|
||||
{"label": "1", "value": 1},
|
||||
{"label": "true", "value": true},
|
||||
{"label": "false", "value": false},
|
||||
{"label": "emptyString", "value": ''},
|
||||
{"label": "stringZero", "value": '0'},
|
||||
{"label": "stringOne", "value": '1'}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "submit",
|
||||
"label": "SubmitBtn",
|
||||
"primary": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"label": "ID"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"label": "Engine version engine"
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({fetcher: mockFetcher})
|
||||
)
|
||||
);
|
||||
|
||||
const select = container.querySelector('.cxd-Select')!;
|
||||
const submitBtn = container.querySelector("button[type='submit']")!;
|
||||
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
let options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[0]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual(0);
|
||||
|
||||
/** 从 0 -> false 查询成功 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[3]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual(false);
|
||||
|
||||
/** 从 false -> '' 查询成功 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[4]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual('');
|
||||
|
||||
/** 从 '' -> 0 查询成功 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[0]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual(0);
|
||||
|
||||
/** 切换到1 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[1]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual(1);
|
||||
|
||||
/** 从 1 -> true 查询成功 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[2]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual(true);
|
||||
|
||||
/** 从 true -> '1' 查询成功 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[6]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual('1');
|
||||
|
||||
/** 切换到false */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[3]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual(false);
|
||||
|
||||
/** 从 false -> '0' 查询成功 */
|
||||
fireEvent.click(select);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-Select-option-content');
|
||||
fireEvent.click(options[5]);
|
||||
await wait(200);
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(200);
|
||||
expect(keyword).toEqual('0');
|
||||
}, 7000);
|
||||
|
@ -119,7 +119,7 @@ exports[`Renderer:static 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:static 2`] = `
|
||||
exports[`Renderer:static2 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Panel cxd-Panel--default cxd-Panel--form"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,12 +2,13 @@
|
||||
* 组件名称:NestedSelect 级联选择器
|
||||
* 单测内容:
|
||||
* 01. maxTagLength
|
||||
* 02. onlyLeaf
|
||||
*/
|
||||
|
||||
import {render, cleanup, waitFor} from '@testing-library/react';
|
||||
import {render, cleanup, waitFor, fireEvent} from '@testing-library/react';
|
||||
import '../../../src';
|
||||
import {render as amisRender} from '../../../src';
|
||||
import {makeEnv} from '../../helper';
|
||||
import {makeEnv, wait} from '../../helper';
|
||||
import {clearStoresCache} from '../../../src';
|
||||
|
||||
afterEach(() => {
|
||||
@ -113,3 +114,107 @@ describe('Renderer:NestedSelect', () => {
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe.only('Renderer:NestedSelect with onlyLeaf', () => {
|
||||
test('single selection', async () => {
|
||||
const optionWithNoChild = 'OptionWithNoChild';
|
||||
const optionWithChild = 'OptionWithChild';
|
||||
const {container, queryByText} = await setupNestedSelect({
|
||||
"onlyLeaf": true,
|
||||
"options": [
|
||||
{"label": "选项A", "value": "A"},
|
||||
{"label": optionWithNoChild, "value": "B", "children": []},
|
||||
{
|
||||
"label": optionWithChild,
|
||||
"value": "C",
|
||||
"children": [
|
||||
{"label": "选项c1", "value": "c1"},
|
||||
{"label": "选项c2", "value": "c2"}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const trigger = container.querySelector('.cxd-ResultBox');
|
||||
expect(trigger).toBeInTheDocument();
|
||||
|
||||
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
|
||||
const parentNum = container.querySelectorAll('.cxd-NestedSelect-optionArrowRight')?.length ?? 0;
|
||||
expect(parentNum).toEqual(1);
|
||||
|
||||
let options = container.querySelectorAll('.cxd-NestedSelect-optionLabel');
|
||||
expect(options.length).toEqual(3);
|
||||
|
||||
/** onlyLeaf开启后,children为空数组的选项也可以选择 */
|
||||
fireEvent.click(options[1]);
|
||||
await wait(300);
|
||||
expect(queryByText(optionWithNoChild)!).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-NestedSelect-optionLabel');
|
||||
fireEvent.click(options[2]);
|
||||
await wait(300);
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
expect(queryByText(optionWithNoChild)!).toBeInTheDocument();
|
||||
/** onlyLeaf开启后,children非空的选项无法选择 */
|
||||
expect(queryByText(optionWithChild)).toBeNull();
|
||||
});
|
||||
|
||||
test('single selection', async () => {
|
||||
const optionWithNoChild = 'OptionWithNoChild';
|
||||
const optionWithChild = 'OptionWithChild';
|
||||
const {container, queryByText} = await setupNestedSelect({
|
||||
"onlyLeaf": true,
|
||||
"multiple": true,
|
||||
"options": [
|
||||
{"label": "选项A", "value": "A"},
|
||||
{"label": optionWithNoChild, "value": "B", "children": []},
|
||||
{
|
||||
"label": optionWithChild,
|
||||
"value": "C",
|
||||
"children": [
|
||||
{"label": "选项c1", "value": "c1"},
|
||||
{"label": "选项c2", "value": "c2"}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const trigger = container.querySelector('.cxd-ResultBox');
|
||||
expect(trigger).toBeInTheDocument();
|
||||
|
||||
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
|
||||
const parentNum = container.querySelectorAll('.cxd-NestedSelect-optionArrowRight')?.length ?? 0;
|
||||
expect(parentNum).toEqual(1);
|
||||
|
||||
let options = container.querySelectorAll('.cxd-NestedSelect-optionLabel');
|
||||
expect(options.length).toEqual(3);
|
||||
|
||||
/** onlyLeaf开启后,children为空数组的选项也可以选择 */
|
||||
fireEvent.click(options[1]);
|
||||
await wait(300);
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
expect(queryByText(optionWithNoChild)!).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
options = container.querySelectorAll('.cxd-NestedSelect-optionLabel');
|
||||
fireEvent.click(options[2]);
|
||||
await wait(300);
|
||||
fireEvent.click(trigger!);
|
||||
await wait(200);
|
||||
expect(queryByText(optionWithNoChild)!).toBeInTheDocument();
|
||||
/** onlyLeaf开启后,children非空的选项无法选择 */
|
||||
expect(queryByText(optionWithChild)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -66,7 +66,7 @@ test('Renderer:static', async () => {
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Renderer:static', async () => {
|
||||
test('Renderer:static2', async () => {
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
@ -189,3 +189,39 @@ test('Renderer:staticOn', async () => {
|
||||
const text = getByText('123');
|
||||
expect(text).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Renderer:staticInColumn', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'crud',
|
||||
source: '${items}',
|
||||
columns: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'a',
|
||||
label: 'a',
|
||||
static: true,
|
||||
quickEdit: {
|
||||
type: 'input-text',
|
||||
mode: 'inline'
|
||||
}
|
||||
}
|
||||
],
|
||||
submitText: null,
|
||||
actions: []
|
||||
},
|
||||
{
|
||||
data: {
|
||||
items: [{a: '1'}]
|
||||
}
|
||||
},
|
||||
makeEnv()
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
|
||||
expect(container.querySelector('input[name="a"]')).toBeInTheDocument();
|
||||
expect((container.querySelector('input[name="a"]') as any).value).toBe('1');
|
||||
});
|
||||
|
@ -1392,7 +1392,6 @@ test('Renderer:transfer search highlight', async () => {
|
||||
});
|
||||
|
||||
test('Renderer:transfer tree search', async () => {
|
||||
|
||||
const onSubmit = jest.fn();
|
||||
const {container, findByText, getByText} = render(
|
||||
amisRender(
|
||||
@ -1486,7 +1485,7 @@ test('Renderer:transfer tree search', async () => {
|
||||
});
|
||||
|
||||
await(300);
|
||||
|
||||
|
||||
const libai = getByText('李白');
|
||||
expect(libai).not.toBeNull();
|
||||
fireEvent.click(libai);
|
||||
@ -1501,4 +1500,310 @@ test('Renderer:transfer tree search', async () => {
|
||||
expect(onSubmit.mock.calls[0][0]).toEqual({
|
||||
transfer: "caocao,libai"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Renderer:Transfer with pagination', async () => {
|
||||
const mockData = [
|
||||
{
|
||||
"label": "Laura Lewis",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"label": "David Gonzalez",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Rodriguez",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"label": "Sarah Young",
|
||||
"value": "4"
|
||||
},
|
||||
{
|
||||
"label": "James Jones",
|
||||
"value": "5"
|
||||
},
|
||||
{
|
||||
"label": "Larry Robinson",
|
||||
"value": "6"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Perez",
|
||||
"value": "7"
|
||||
},
|
||||
{
|
||||
"label": "Sharon Davis",
|
||||
"value": "8"
|
||||
},
|
||||
{
|
||||
"label": "Kenneth Anderson",
|
||||
"value": "9"
|
||||
},
|
||||
{
|
||||
"label": "Deborah Lewis",
|
||||
"value": "10"
|
||||
},
|
||||
{
|
||||
"label": "Jennifer Lewis",
|
||||
"value": "11"
|
||||
},
|
||||
{
|
||||
"label": "Laura Miller",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"label": "Larry Harris",
|
||||
"value": "13"
|
||||
},
|
||||
{
|
||||
"label": "Patricia Robinson",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"label": "Mark Davis",
|
||||
"value": "15"
|
||||
},
|
||||
{
|
||||
"label": "Jessica Harris",
|
||||
"value": "16"
|
||||
},
|
||||
{
|
||||
"label": "Anna Brown",
|
||||
"value": "17"
|
||||
},
|
||||
{
|
||||
"label": "Lisa Young",
|
||||
"value": "18"
|
||||
},
|
||||
{
|
||||
"label": "Donna Williams",
|
||||
"value": "19"
|
||||
},
|
||||
{
|
||||
"label": "Shirley Davis",
|
||||
"value": "20"
|
||||
}
|
||||
];
|
||||
const fetcher = jest.fn().mockImplementation((api) => {
|
||||
const perPage = 10; /** 锁死10个方便测试 */
|
||||
const page = Number(api.query.page || 1);
|
||||
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
count: mockData.length,
|
||||
page: page,
|
||||
items: mockData.concat().splice((page - 1) * perPage, perPage)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"label": "默认",
|
||||
"type": "transfer",
|
||||
"name": "transfer",
|
||||
"joinValues": false,
|
||||
"extractValue": false,
|
||||
"source": "/api/mock2/options/transfer?page=${page}&perPage=${perPage}",
|
||||
"pagination": {
|
||||
"enable": true,
|
||||
"layout": ["pager", "perpage", "total"],
|
||||
"popOverContainerSelector": ".cxd-Panel--form"
|
||||
},
|
||||
"value": [
|
||||
{"label": "Laura Lewis", "value": "1", id: 1},
|
||||
{"label": "Christopher Rodriguez", "value": "3", id: 3},
|
||||
{"label": "Laura Miller", "value": "12", id: 12},
|
||||
{"label": "Patricia Robinson", "value": "14", id: 14}
|
||||
]
|
||||
}
|
||||
]
|
||||
}, {}, makeEnv({fetcher})));
|
||||
|
||||
await wait(500);
|
||||
expect(container.querySelector('.cxd-Transfer-footer-pagination')).toBeInTheDocument();
|
||||
|
||||
const checkboxes = container.querySelectorAll('input[type=checkbox]')!;
|
||||
expect(checkboxes.length).toEqual(11); /** 包括顶部全选 */
|
||||
expect((checkboxes[1] as HTMLInputElement)?.checked).toEqual(true);
|
||||
expect((checkboxes[2] as HTMLInputElement)?.checked).toEqual(false);
|
||||
expect((checkboxes[3] as HTMLInputElement)?.checked).toEqual(true);
|
||||
expect((checkboxes[4] as HTMLInputElement)?.checked).toEqual(false);
|
||||
|
||||
const nextBtn = container.querySelector('.cxd-Pagination-next')!;
|
||||
fireEvent.click(nextBtn);
|
||||
await wait(500);
|
||||
|
||||
const checkboxes2 = container.querySelectorAll('input[type=checkbox]')!;
|
||||
expect(checkboxes2.length).toEqual(11);
|
||||
expect((checkboxes2[1] as HTMLInputElement)?.checked).toEqual(false);
|
||||
expect((checkboxes2[2] as HTMLInputElement)?.checked).toEqual(true);
|
||||
expect((checkboxes2[3] as HTMLInputElement)?.checked).toEqual(false);
|
||||
expect((checkboxes2[4] as HTMLInputElement)?.checked).toEqual(true);
|
||||
})
|
||||
|
||||
test.only('Renderer:Transfer with pagination and data source from data scope', async () => {
|
||||
const mockData = [
|
||||
{
|
||||
"label": "Laura Lewis",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"label": "David Gonzalez",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Rodriguez",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"label": "Sarah Young",
|
||||
"value": "4"
|
||||
},
|
||||
{
|
||||
"label": "James Jones",
|
||||
"value": "5"
|
||||
},
|
||||
{
|
||||
"label": "Larry Robinson",
|
||||
"value": "6"
|
||||
},
|
||||
{
|
||||
"label": "Christopher Perez",
|
||||
"value": "7"
|
||||
},
|
||||
{
|
||||
"label": "Sharon Davis",
|
||||
"value": "8"
|
||||
},
|
||||
{
|
||||
"label": "Kenneth Anderson",
|
||||
"value": "9"
|
||||
},
|
||||
{
|
||||
"label": "Deborah Lewis",
|
||||
"value": "10"
|
||||
},
|
||||
{
|
||||
"label": "Jennifer Lewis",
|
||||
"value": "11"
|
||||
},
|
||||
{
|
||||
"label": "Laura Miller",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"label": "Larry Harris",
|
||||
"value": "13"
|
||||
},
|
||||
{
|
||||
"label": "Patricia Robinson",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"label": "Mark Davis",
|
||||
"value": "15"
|
||||
},
|
||||
{
|
||||
"label": "Jessica Harris",
|
||||
"value": "16"
|
||||
},
|
||||
{
|
||||
"label": "Anna Brown",
|
||||
"value": "17"
|
||||
},
|
||||
{
|
||||
"label": "Lisa Young",
|
||||
"value": "18"
|
||||
},
|
||||
{
|
||||
"label": "Donna Williams",
|
||||
"value": "19"
|
||||
},
|
||||
{
|
||||
"label": "Shirley Davis",
|
||||
"value": "20"
|
||||
}
|
||||
];
|
||||
const fetcher = jest.fn().mockImplementation((api) => {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
count: mockData.length,
|
||||
items: mockData
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"type": "service",
|
||||
"api": {
|
||||
"url": "/api/mock2/options/loadDataOnce",
|
||||
"method": "get",
|
||||
"responseData": {
|
||||
"transferOptions": "${items}"
|
||||
}
|
||||
},
|
||||
body: [
|
||||
{
|
||||
"label": "默认",
|
||||
"type": "transfer",
|
||||
"name": "transfer",
|
||||
"joinValues": false,
|
||||
"extractValue": false,
|
||||
"source": "${transferOptions}",
|
||||
"pagination": {
|
||||
"enable": true,
|
||||
"layout": ["pager", "perpage", "total"],
|
||||
"popOverContainerSelector": ".cxd-Panel--form"
|
||||
},
|
||||
"value": [
|
||||
{"label": "Laura Lewis", "value": "1", id: 1},
|
||||
{"label": "Christopher Rodriguez", "value": "3", id: 3},
|
||||
{"label": "Laura Miller", "value": "12", id: 12},
|
||||
{"label": "Patricia Robinson", "value": "14", id: 14}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}, {}, makeEnv({fetcher})));
|
||||
|
||||
await wait(500);
|
||||
expect(container.querySelector('.cxd-Transfer-footer-pagination')).toBeInTheDocument();
|
||||
|
||||
const checkboxes = container.querySelectorAll('input[type=checkbox]')!;
|
||||
expect(checkboxes.length).toEqual(11); /** 包括顶部全选 */
|
||||
expect((checkboxes[1] as HTMLInputElement)?.checked).toEqual(true);
|
||||
expect((checkboxes[2] as HTMLInputElement)?.checked).toEqual(false);
|
||||
expect((checkboxes[3] as HTMLInputElement)?.checked).toEqual(true);
|
||||
expect((checkboxes[4] as HTMLInputElement)?.checked).toEqual(false);
|
||||
|
||||
const nextBtn = container.querySelector('.cxd-Pagination-next')!;
|
||||
fireEvent.click(nextBtn);
|
||||
await wait(500);
|
||||
|
||||
const checkboxes2 = container.querySelectorAll('input[type=checkbox]')!;
|
||||
expect(checkboxes2.length).toEqual(11);
|
||||
expect((checkboxes2[1] as HTMLInputElement)?.checked).toEqual(false);
|
||||
expect((checkboxes2[2] as HTMLInputElement)?.checked).toEqual(true);
|
||||
expect((checkboxes2[3] as HTMLInputElement)?.checked).toEqual(false);
|
||||
expect((checkboxes2[4] as HTMLInputElement)?.checked).toEqual(true);
|
||||
})
|
||||
|
@ -12,6 +12,10 @@
|
||||
* 9. href
|
||||
* 10. 作为表单项
|
||||
* 11. clickAction
|
||||
* 12. click事件
|
||||
* 13. mouseenter / mouseleave 事件
|
||||
* 14. preview 预览动作
|
||||
* 15. zoom & maxScale & minScale 调整图片比例动作
|
||||
*
|
||||
* * 组件名称:Images 图片集
|
||||
* 内容说明:images 与 image 使用组件相同,相同属性不重复测试了
|
||||
@ -20,7 +24,7 @@
|
||||
* 2. enlargeAble & originalSrc & source & title & description
|
||||
*/
|
||||
|
||||
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, wait} from '../helper';
|
||||
@ -334,7 +338,228 @@ describe('Renderer:image', () => {
|
||||
})
|
||||
);
|
||||
fireEvent.click(container.querySelector('.cxd-Image-thumbWrap')!);
|
||||
expect(getByText('这是一个弹框')!).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(getByText('这是一个弹框')!).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('image:click', async () => {
|
||||
const notify = jest.fn();
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'image',
|
||||
src: 'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80',
|
||||
class: 'cursor-pointer',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msgType: 'info',
|
||||
msg: '派发点击事件'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
mouseenter: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msgType: 'info',
|
||||
msg: '派发鼠标移入事件'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
mouseleave: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msgType: 'info',
|
||||
msg: '派发鼠标移出事件'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
notify,
|
||||
session: 'image-test-action-1'
|
||||
})
|
||||
)
|
||||
);
|
||||
fireEvent.click(container.querySelector('.cxd-Image-thumbWrap')!);
|
||||
await waitFor(() => {
|
||||
expect(notify).toHaveBeenCalledWith('info', '派发点击事件', {
|
||||
msg: '派发点击事件',
|
||||
msgType: 'info'
|
||||
});
|
||||
});
|
||||
fireEvent.mouseEnter(container.querySelector('.cxd-Image-thumbWrap')!);
|
||||
await waitFor(() => {
|
||||
expect(notify).toHaveBeenCalledWith('info', '派发鼠标移入事件', {
|
||||
msg: '派发鼠标移入事件',
|
||||
msgType: 'info'
|
||||
});
|
||||
});
|
||||
fireEvent.mouseLeave(container.querySelector('.cxd-Image-thumbWrap')!);
|
||||
await waitFor(() => {
|
||||
expect(notify).toHaveBeenCalledWith('info', '派发鼠标移出事件', {
|
||||
msg: '派发鼠标移出事件',
|
||||
msgType: 'info'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('image:preview 预览动作', async () => {
|
||||
const {container, getByText, baseElement} = render(
|
||||
amisRender({
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'image',
|
||||
id: 'previewImage',
|
||||
src: 'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80',
|
||||
originalSrc:
|
||||
'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg'
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '预览图片',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'preview',
|
||||
componentId: 'previewImage'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
expect(container).toMatchSnapshot();
|
||||
fireEvent.click(getByText('预览图片'));
|
||||
expect(baseElement.querySelector('.cxd-ImageGallery')!).toBeInTheDocument();
|
||||
expect(
|
||||
baseElement.querySelector('.cxd-ImageGallery .cxd-ImageGallery-main img')!
|
||||
).toHaveAttribute(
|
||||
'src',
|
||||
'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg'
|
||||
);
|
||||
});
|
||||
|
||||
test('image:zoom & maxScale & minScale 调整图片比例动作', async () => {
|
||||
const {container, getByText, baseElement} = render(
|
||||
amisRender({
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'image',
|
||||
id: 'zoomImage',
|
||||
src: 'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80',
|
||||
originalSrc:
|
||||
'https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg',
|
||||
maxScale: 200,
|
||||
minScale: 20
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '放大图片',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'zoom',
|
||||
args: {
|
||||
scale: 50
|
||||
},
|
||||
componentId: 'zoomImage'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '缩小图片',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'zoom',
|
||||
args: {
|
||||
scale: -50
|
||||
},
|
||||
componentId: 'zoomImage'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
const imgIns = baseElement.querySelector('.cxd-ImageField--thumb')!;
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(1)'
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('放大图片'));
|
||||
await waitFor(() => {
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(1.5)'
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('缩小图片'));
|
||||
await waitFor(() => {
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(1)'
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('放大图片'));
|
||||
fireEvent.click(getByText('放大图片'));
|
||||
await waitFor(() => {
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(2)'
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('放大图片'));
|
||||
await waitFor(() => {
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(2)'
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('缩小图片'));
|
||||
fireEvent.click(getByText('缩小图片'));
|
||||
fireEvent.click(getByText('缩小图片'));
|
||||
fireEvent.click(getByText('缩小图片'));
|
||||
await waitFor(() => {
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(0.2)'
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(getByText('缩小图片'));
|
||||
await waitFor(() => {
|
||||
expect(imgIns).toHaveStyle({
|
||||
transform: 'scale(0.2)'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -56,6 +56,7 @@ exports[`Renderer:image image as form item 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb no-border"
|
||||
@ -103,6 +104,7 @@ exports[`Renderer:image image:basic 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb show"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -138,6 +140,7 @@ exports[`Renderer:image image:enlargeAble & originalSrc & enlargeTitle & showToo
|
||||
<div>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -178,6 +181,7 @@ exports[`Renderer:image image:enlargeAble & originalSrc & enlargeTitle & showToo
|
||||
<div>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -231,6 +235,7 @@ exports[`Renderer:image image:href 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<a
|
||||
class="cxd-Link"
|
||||
@ -263,10 +268,62 @@ exports[`Renderer:image image:href 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:image image:preview 预览动作 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image-thumbWrap"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image-thumb cxd-Image-thumb--contain cxd-Image-thumb--1-1"
|
||||
>
|
||||
<img
|
||||
class="cxd-Image-image"
|
||||
src="https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="cxd-Button cxd-Button--default cxd-Button--size-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
预览图片
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:image image:title & imageCaption 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -308,6 +365,7 @@ exports[`Renderer:image image:width & height 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -330,6 +388,65 @@ exports[`Renderer:image image:width & height 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:image image:zoom & maxScale & minScale 调整图片比例动作 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Page"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
class="cxd-Page-body"
|
||||
role="page-body"
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image-thumbWrap"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image-thumb cxd-Image-thumb--contain cxd-Image-thumb--1-1"
|
||||
>
|
||||
<img
|
||||
class="cxd-Image-image"
|
||||
src="https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
缩小图片
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Renderer:images images:basic 1`] = `
|
||||
<div>
|
||||
<div
|
||||
|
@ -7090,6 +7090,7 @@ exports[`Renderer:table list 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -7331,6 +7332,7 @@ exports[`Renderer:table list 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
@ -7692,6 +7694,7 @@ exports[`Renderer:table list 1`] = `
|
||||
>
|
||||
<div
|
||||
class="cxd-ImageField cxd-ImageField--thumb"
|
||||
style="transform: scale(1);"
|
||||
>
|
||||
<div
|
||||
class="cxd-Image cxd-Image--thumb"
|
||||
|
@ -1949,7 +1949,13 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
}
|
||||
|
||||
renderPagination(toolbar: SchemaNode) {
|
||||
const {store, render, classnames: cx, alwaysShowPagination} = this.props;
|
||||
const {
|
||||
store,
|
||||
render,
|
||||
classnames: cx,
|
||||
alwaysShowPagination,
|
||||
perPageAvailable
|
||||
} = this.props;
|
||||
const {page, lastPage} = store;
|
||||
|
||||
if (
|
||||
@ -1971,6 +1977,11 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
| 'showPerPage'
|
||||
> = {};
|
||||
|
||||
// 下发 perPageAvailable
|
||||
if (Array.isArray(perPageAvailable)) {
|
||||
extraProps.perPageAvailable = perPageAvailable;
|
||||
}
|
||||
|
||||
/** 优先级:showPageInput显性配置 > (lastPage > 9) */
|
||||
if (typeof toolbar !== 'string') {
|
||||
Object.assign(extraProps, toolbar);
|
||||
@ -2218,7 +2229,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
toolbar.align || (type === 'pagination' ? 'right' : 'left');
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
key={toolbar.id || index}
|
||||
className={cx(
|
||||
'Crud-toolbar-item',
|
||||
align ? `Crud-toolbar-item--${align}` : '',
|
||||
|
@ -11,7 +11,9 @@ import {
|
||||
difference,
|
||||
ucFirst,
|
||||
autobind,
|
||||
createObject
|
||||
createObject,
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {
|
||||
isPureVariable,
|
||||
@ -923,7 +925,10 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||
translate: __,
|
||||
loading = false,
|
||||
loadingConfig,
|
||||
env
|
||||
env,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
themeCss
|
||||
} = this.props;
|
||||
|
||||
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
|
||||
@ -973,9 +978,15 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||
return (
|
||||
<div
|
||||
ref={this.bodyRef}
|
||||
className={cx('Cards', className, {
|
||||
'Cards--unsaved': !!store.modified || !!store.moved
|
||||
})}
|
||||
className={cx(
|
||||
'Cards',
|
||||
className,
|
||||
{
|
||||
'Cards--unsaved': !!store.modified || !!store.moved
|
||||
},
|
||||
setThemeClassName('baseControlClassName', id, themeCss),
|
||||
setThemeClassName('wrapperCustomStyle', id, wrapperCustomStyle)
|
||||
)}
|
||||
style={buildStyle(style, data)}
|
||||
>
|
||||
{affixHeader ? (
|
||||
@ -1007,6 +1018,20 @@ export default class Cards extends React.Component<GridProps, object> {
|
||||
|
||||
{footer}
|
||||
<Spinner loadingConfig={loadingConfig} overlay show={loading} />
|
||||
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName'
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,12 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps, buildStyle, isPureVariable} from 'amis-core';
|
||||
import {
|
||||
CustomStyle,
|
||||
Renderer,
|
||||
RendererProps,
|
||||
buildStyle,
|
||||
isPureVariable,
|
||||
setThemeClassName
|
||||
} from 'amis-core';
|
||||
import {Schema} from 'amis-core';
|
||||
import {resolveVariable, resolveVariableAndFilter} from 'amis-core';
|
||||
import {createObject, getPropValue, isObject} from 'amis-core';
|
||||
@ -96,7 +103,11 @@ export default class Each extends React.Component<EachProps> {
|
||||
indexKeyName,
|
||||
placeholder,
|
||||
classnames: cx,
|
||||
translate: __
|
||||
translate: __,
|
||||
env,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
themeCss
|
||||
} = this.props;
|
||||
|
||||
const value = getPropValue(this.props, props =>
|
||||
@ -124,7 +135,14 @@ export default class Each extends React.Component<EachProps> {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx('Each', className)} style={buildStyle(style, data)}>
|
||||
<div
|
||||
className={cx(
|
||||
'Each',
|
||||
className,
|
||||
setThemeClassName('baseControlClassName', id, themeCss)
|
||||
)}
|
||||
style={buildStyle(style, data)}
|
||||
>
|
||||
{Array.isArray(arr) && arr.length && items ? (
|
||||
arr.map((item: any, index: number) => (
|
||||
<EachItem
|
||||
@ -144,6 +162,20 @@ export default class Each extends React.Component<EachProps> {
|
||||
{render('placeholder', __(placeholder))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName'
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ import {
|
||||
resolveEventData,
|
||||
ApiObject,
|
||||
FormHorizontal,
|
||||
evalExpressionWithConditionBuilder
|
||||
evalExpressionWithConditionBuilder,
|
||||
IFormStore,
|
||||
getVariable,
|
||||
IFormItemStore
|
||||
} from 'amis-core';
|
||||
import {ActionObject, Api} from 'amis-core';
|
||||
import {ComboStore, IComboStore} from 'amis-core';
|
||||
@ -37,7 +40,11 @@ import {isEffectiveApi, str2AsyncFunction} from 'amis-core';
|
||||
import {Alert2} from 'amis-ui';
|
||||
import memoize from 'lodash/memoize';
|
||||
import {Icon} from 'amis-ui';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
import {
|
||||
isAlive,
|
||||
clone as cloneModel,
|
||||
destroy as destroyModel
|
||||
} from 'mobx-state-tree';
|
||||
import {
|
||||
FormBaseControlSchema,
|
||||
SchemaApi,
|
||||
@ -48,7 +55,6 @@ import {
|
||||
import {ListenerAction} from 'amis-core';
|
||||
import type {SchemaTokenizeableString} from '../../Schema';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import {isMobile} from 'amis-core';
|
||||
|
||||
export type ComboCondition = {
|
||||
test: string;
|
||||
@ -395,6 +401,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
this.dragTipRef = this.dragTipRef.bind(this);
|
||||
this.flush = this.flush.bind(this);
|
||||
this.handleComboTypeChange = this.handleComboTypeChange.bind(this);
|
||||
this.handleSubFormValid = this.handleSubFormValid.bind(this);
|
||||
this.defaultValue = {
|
||||
...props.scaffold
|
||||
};
|
||||
@ -797,6 +804,11 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
);
|
||||
}
|
||||
|
||||
handleSubFormValid(valid: boolean, {index}: any) {
|
||||
const {store} = this.props;
|
||||
store.setMemberValid(valid, index);
|
||||
}
|
||||
|
||||
handleFormInit(values: any, {index}: any) {
|
||||
const {
|
||||
syncDefaultValue,
|
||||
@ -806,9 +818,15 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
formInited,
|
||||
onChange,
|
||||
submitOnChange,
|
||||
setPrinstineValue
|
||||
setPrinstineValue,
|
||||
formItem
|
||||
} = this.props;
|
||||
|
||||
// 已经开始验证了,那么打开成员的时候,就要验证一下。
|
||||
if (formItem?.validated) {
|
||||
this.subForms[index]?.validate(true, false, false);
|
||||
}
|
||||
|
||||
this.subFormDefaultValues.push({
|
||||
index,
|
||||
values,
|
||||
@ -881,7 +899,13 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
}
|
||||
|
||||
validate(): any {
|
||||
const {messages, nullable, translate: __} = this.props;
|
||||
const {
|
||||
messages,
|
||||
nullable,
|
||||
value: rawValue,
|
||||
translate: __,
|
||||
store
|
||||
} = this.props;
|
||||
const value = this.getValueAsArray();
|
||||
const minLength = this.resolveVariableProps(this.props, 'minLength');
|
||||
const maxLength = this.resolveVariableProps(this.props, 'maxLength');
|
||||
@ -896,18 +920,62 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
(messages && messages.maxLengthValidateFailed) || 'Combo.maxLength',
|
||||
{maxLength}
|
||||
);
|
||||
} else if (this.subForms.length && (!nullable || value)) {
|
||||
return Promise.all(this.subForms.map(item => item.validate())).then(
|
||||
values => {
|
||||
if (~values.indexOf(false)) {
|
||||
return __(
|
||||
(messages && messages.validateFailed) || 'validateFailed'
|
||||
);
|
||||
}
|
||||
} else if (nullable && !rawValue) {
|
||||
return; // 不校验
|
||||
} else if (value.length) {
|
||||
return Promise.all(
|
||||
value.map(async (values: any, index: number) => {
|
||||
const subForm = this.subForms[index];
|
||||
if (subForm) {
|
||||
return subForm.validate(true, false, false);
|
||||
} else {
|
||||
// 那些还没有渲染出来的数据
|
||||
// 因为有可能存在分页,有可能存在懒加载,所以没办法直接用 subForm 去校验了
|
||||
const subForm = this.subForms[Object.keys(this.subForms)[0] as any];
|
||||
if (subForm) {
|
||||
const form: IFormStore = subForm.props.store;
|
||||
let valid = false;
|
||||
for (let formitem of form.items) {
|
||||
const cloned: IFormItemStore = cloneModel(formitem);
|
||||
let value: any = getVariable(values, formitem.name, false);
|
||||
|
||||
return;
|
||||
if (formitem.extraName) {
|
||||
value = [
|
||||
getVariable(values, formitem.name, false),
|
||||
getVariable(values, formitem.extraName, false)
|
||||
];
|
||||
}
|
||||
|
||||
cloned.changeTmpValue(value, 'dataChanged');
|
||||
valid = await cloned.validate(values);
|
||||
destroyModel(cloned);
|
||||
if (valid === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
store.setMemberValid(valid, index);
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
})
|
||||
).then(values => {
|
||||
if (~values.indexOf(false)) {
|
||||
return __((messages && messages.validateFailed) || 'validateFailed');
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
});
|
||||
} else if (this.subForms.length) {
|
||||
return Promise.all(
|
||||
this.subForms.map(item => item.validate(true, false, false))
|
||||
).then(values => {
|
||||
if (~values.indexOf(false)) {
|
||||
return __((messages && messages.validateFailed) || 'validateFailed');
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,6 +1321,12 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
// 不能按需渲染,因为 unique 会失效。
|
||||
mountOnEnter={!hasUnique}
|
||||
unmountOnExit={false}
|
||||
className={
|
||||
store.memberValidMap[index] === false ? 'has-error' : ''
|
||||
}
|
||||
tabClassName={
|
||||
store.memberValidMap[index] === false ? 'has-error' : ''
|
||||
}
|
||||
>
|
||||
{condition && typeSwitchable !== false ? (
|
||||
<div className={cx('Combo-itemTag')}>
|
||||
@ -1485,7 +1559,8 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
itemClassName,
|
||||
itemsWrapperClassName,
|
||||
static: isStatic,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
store
|
||||
} = this.props;
|
||||
|
||||
let items = this.props.items;
|
||||
@ -1543,7 +1618,11 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(`Combo-item`, itemClassName)}
|
||||
className={cx(
|
||||
`Combo-item`,
|
||||
itemClassName,
|
||||
store.memberValidMap[index] === false ? 'has-error' : ''
|
||||
)}
|
||||
key={this.keys[index]}
|
||||
>
|
||||
{!isStatic && !disabled && draggable && thelist.length > 1 ? (
|
||||
@ -1622,7 +1701,8 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
nullable,
|
||||
translate: __,
|
||||
itemClassName,
|
||||
mobileUI
|
||||
mobileUI,
|
||||
store
|
||||
} = this.props;
|
||||
|
||||
let items = this.props.items;
|
||||
@ -1646,7 +1726,13 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
disabled ? 'is-disabled' : ''
|
||||
)}
|
||||
>
|
||||
<div className={cx(`Combo-item`, itemClassName)}>
|
||||
<div
|
||||
className={cx(
|
||||
`Combo-item`,
|
||||
itemClassName,
|
||||
store.memberValidMap[0] === false ? 'has-error' : ''
|
||||
)}
|
||||
>
|
||||
{condition && typeSwitchable !== false ? (
|
||||
<div className={cx('Combo-itemTag')}>
|
||||
<label>{__('Combo.type')}</label>
|
||||
@ -1715,11 +1801,13 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
className: cx(`Combo-form`, formClassName)
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
disabled: disabled,
|
||||
static: isStatic,
|
||||
data,
|
||||
onChange: this.handleSingleFormChange,
|
||||
ref: this.makeFormRef(0),
|
||||
onValidChange: this.handleSubFormValid,
|
||||
onInit: this.handleSingleFormInit,
|
||||
canAccessSuperData,
|
||||
formStore: undefined,
|
||||
@ -1749,6 +1837,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
onAction: this.handleAction,
|
||||
onRadioChange: this.handleRadioChange,
|
||||
ref: this.makeFormRef(index),
|
||||
onValidChange: this.handleSubFormValid,
|
||||
canAccessSuperData,
|
||||
lazyChange: changeImmediately ? false : true,
|
||||
formLazyChange: false,
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
ActionObject,
|
||||
isMobile,
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter
|
||||
resolveVariableAndFilter,
|
||||
isNumeric
|
||||
} from 'amis-core';
|
||||
import {Range as InputRange, NumberInput, Icon} from 'amis-ui';
|
||||
import {FormBaseControlSchema, SchemaObject} from '../../Schema';
|
||||
@ -703,6 +704,11 @@ export default class RangeControl extends React.PureComponent<
|
||||
renderMarks &&
|
||||
(renderMarks[key] = render(region, item as SchemaObject));
|
||||
}
|
||||
|
||||
/** 过滤掉不合法的值(合法的值是数字 & 百分数) */
|
||||
if (renderMarks && !isNumeric(key.replace(/%$/, ''))) {
|
||||
delete renderMarks[key];
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -299,6 +299,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
subFormItems: any = {};
|
||||
rowPrinstine: Array<any> = [];
|
||||
editting: any = {};
|
||||
table: any;
|
||||
|
||||
constructor(props: TableProps) {
|
||||
super(props);
|
||||
@ -587,6 +588,10 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
);
|
||||
|
||||
return;
|
||||
} else if (actionType === 'initDrag') {
|
||||
const tableStore = this.table?.props?.store;
|
||||
tableStore?.stopDragging();
|
||||
tableStore?.toggleDragging();
|
||||
}
|
||||
return onAction && onAction(action, ctx, ...rest);
|
||||
}
|
||||
@ -1550,6 +1555,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
while (ref && ref.getWrappedInstance) {
|
||||
ref = ref.getWrappedInstance();
|
||||
}
|
||||
this.table = ref;
|
||||
}
|
||||
|
||||
computedAddBtnDisabled() {
|
||||
@ -1586,7 +1592,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
static: isStatic,
|
||||
showFooterAddBtn,
|
||||
footerAddBtn,
|
||||
toolbarClassName
|
||||
toolbarClassName,
|
||||
onEvent
|
||||
} = this.props;
|
||||
const maxLength = this.resolveVariableProps(this.props, 'maxLength');
|
||||
|
||||
@ -1621,7 +1628,8 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
prefixRow,
|
||||
affixRow,
|
||||
autoFillHeight,
|
||||
tableContentClassName
|
||||
tableContentClassName,
|
||||
onEvent
|
||||
},
|
||||
{
|
||||
ref: this.tableRef.bind(this),
|
||||
|
@ -142,17 +142,15 @@ export class LocationControl extends React.Component<LocationControlProps> {
|
||||
|
||||
@supportStatic()
|
||||
render() {
|
||||
const {style} = this.props;
|
||||
const {style, env} = this.props;
|
||||
const ak = filter(this.props.ak, this.props.data) || env.locationPickerAK!;
|
||||
return (
|
||||
<div
|
||||
className={this.props.classnames('LocationControl', {
|
||||
'is-mobile': isMobile()
|
||||
})}
|
||||
>
|
||||
<LocationPicker
|
||||
{...this.props}
|
||||
ak={filter(this.props.ak, this.props.data)}
|
||||
/>
|
||||
<LocationPicker {...this.props} ak={ak} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -172,6 +172,11 @@ export default class NestedSelectControl extends React.Component<
|
||||
return !!rendererEvent?.prevented;
|
||||
}
|
||||
|
||||
/** 是否为父节点 */
|
||||
isParentNode(option: Option) {
|
||||
return Array.isArray(option.children) && option.children.length > 0;
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleOutClick(e: React.MouseEvent<any>) {
|
||||
const {options} = this.props;
|
||||
@ -295,7 +300,7 @@ export default class NestedSelectControl extends React.Component<
|
||||
return;
|
||||
}
|
||||
|
||||
if (onlyLeaf && option.children) {
|
||||
if (onlyLeaf && this.isParentNode(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -327,7 +332,7 @@ export default class NestedSelectControl extends React.Component<
|
||||
|
||||
let valueField = this.props.valueField || 'value';
|
||||
|
||||
if (onlyLeaf && !Array.isArray(option) && option.children) {
|
||||
if (onlyLeaf && !Array.isArray(option) && this.isParentNode(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -431,6 +436,7 @@ export default class NestedSelectControl extends React.Component<
|
||||
|
||||
allChecked(options: Options): boolean {
|
||||
const {selectedOptions, withChildren, onlyChildren} = this.props;
|
||||
|
||||
return options.every(option => {
|
||||
if ((withChildren || onlyChildren) && option.children) {
|
||||
return this.allChecked(option.children);
|
||||
@ -683,8 +689,8 @@ export default class NestedSelectControl extends React.Component<
|
||||
if (
|
||||
!selfChecked &&
|
||||
onlyChildren &&
|
||||
option.children &&
|
||||
this.allChecked(option.children)
|
||||
this.isParentNode(option) &&
|
||||
this.allChecked(option.children!)
|
||||
) {
|
||||
selfChecked = true;
|
||||
}
|
||||
@ -728,7 +734,11 @@ export default class NestedSelectControl extends React.Component<
|
||||
</div>
|
||||
|
||||
{option.children && option.children.length ? (
|
||||
<div className={cx('NestedSelect-optionArrowRight')}>
|
||||
<div
|
||||
className={cx('NestedSelect-optionArrowRight', {
|
||||
'is-disabled': nodeDisabled
|
||||
})}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</div>
|
||||
) : null}
|
||||
@ -799,8 +809,8 @@ export default class NestedSelectControl extends React.Component<
|
||||
if (
|
||||
!isChecked &&
|
||||
onlyChildren &&
|
||||
option.children &&
|
||||
this.allChecked(option.children)
|
||||
this.isParentNode(option) &&
|
||||
this.allChecked(option.children!)
|
||||
) {
|
||||
isChecked = true;
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import React from 'react';
|
||||
import find from 'lodash/find';
|
||||
|
||||
import pick from 'lodash/pick';
|
||||
import {isAlive} from 'mobx-state-tree';
|
||||
import {matchSorter} from 'match-sorter';
|
||||
import {
|
||||
OptionsControlProps,
|
||||
OptionsControl,
|
||||
FormOptionsControl,
|
||||
resolveEventData,
|
||||
str2function,
|
||||
getOptionValueBindField
|
||||
} from 'amis-core';
|
||||
import {SpinnerExtraProps, Transfer} from 'amis-ui';
|
||||
import type {Option} from 'amis-core';
|
||||
import {
|
||||
getOptionValueBindField,
|
||||
isEffectiveApi,
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter,
|
||||
autobind,
|
||||
filterTree,
|
||||
string2regExp,
|
||||
@ -20,18 +20,25 @@ import {
|
||||
findTreeIndex,
|
||||
getTree,
|
||||
spliceTree,
|
||||
mapTree
|
||||
mapTree,
|
||||
optionValueCompare,
|
||||
resolveVariable,
|
||||
ActionObject,
|
||||
toNumber
|
||||
} from 'amis-core';
|
||||
import {Spinner} from 'amis-ui';
|
||||
import {optionValueCompare} from 'amis-core';
|
||||
import {resolveVariable} from 'amis-core';
|
||||
import {FormOptionsSchema, SchemaApi, SchemaObject} from '../../Schema';
|
||||
import {Selection as BaseSelection} from 'amis-ui';
|
||||
import {ResultList} from 'amis-ui';
|
||||
import {ActionObject, toNumber} from 'amis-core';
|
||||
import type {ItemRenderStates} from 'amis-ui/lib/components/Selection';
|
||||
import {SpinnerExtraProps, Transfer, Spinner, ResultList} from 'amis-ui';
|
||||
import {
|
||||
FormOptionsSchema,
|
||||
SchemaApi,
|
||||
SchemaObject,
|
||||
SchemaExpression,
|
||||
SchemaClassName
|
||||
} from '../../Schema';
|
||||
import {supportStatic} from './StaticHoc';
|
||||
import {matchSorter} from 'match-sorter';
|
||||
|
||||
import type {ItemRenderStates} from 'amis-ui/lib/components/Selection';
|
||||
import type {Option} from 'amis-core';
|
||||
import type {PaginationSchema} from '../Pagination';
|
||||
|
||||
/**
|
||||
* Transfer
|
||||
@ -161,6 +168,22 @@ export interface TransferControlSchema
|
||||
* 树形模式下,仅选中子节点
|
||||
*/
|
||||
onlyChildren?: boolean;
|
||||
|
||||
/**
|
||||
* 分页配置,selectMode为默认和table才会生效
|
||||
* @since 3.6.0
|
||||
*/
|
||||
pagination?: {
|
||||
/** 是否左侧选项分页,默认不开启 */
|
||||
enable: SchemaExpression;
|
||||
/** 分页组件CSS类名 */
|
||||
className?: SchemaClassName;
|
||||
/** 是否开启前端分页 */
|
||||
loadDataOnce?: boolean;
|
||||
} & Pick<
|
||||
PaginationSchema,
|
||||
'layout' | 'maxButtons' | 'perPageAvailable' | 'popOverContainerSelector'
|
||||
>;
|
||||
}
|
||||
|
||||
export interface BaseTransferProps
|
||||
@ -427,6 +450,30 @@ export class BaseTransferRenderer<
|
||||
return regexp.test(labelTest) || regexp.test(valueTest);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handlePageChange(
|
||||
page: number,
|
||||
perPage?: number,
|
||||
direction?: 'forward' | 'backward'
|
||||
) {
|
||||
const {source, data, formItem, onChange} = this.props;
|
||||
const ctx = createObject(data, {
|
||||
page: page ?? 1,
|
||||
perPage: perPage ?? 10,
|
||||
...(direction ? {pageDir: direction} : {})
|
||||
});
|
||||
|
||||
if (!formItem || !isAlive(formItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPureVariable(source)) {
|
||||
formItem.loadOptionsFromDataScope(source, ctx, onChange);
|
||||
} else if (isEffectiveApi(source, ctx)) {
|
||||
formItem.loadOptions(source, ctx, undefined, false, onChange, false);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
optionItemRender(option: Option, states: ItemRenderStates) {
|
||||
const {menuTpl, render, data} = this.props;
|
||||
@ -544,7 +591,11 @@ export class BaseTransferRenderer<
|
||||
showInvalidMatch,
|
||||
onlyChildren,
|
||||
mobileUI,
|
||||
noResultsText
|
||||
noResultsText,
|
||||
pagination,
|
||||
formItem,
|
||||
env,
|
||||
popOverContainer
|
||||
} = this.props;
|
||||
|
||||
// 目前 LeftOptions 没有接口可以动态加载
|
||||
@ -570,6 +621,7 @@ export class BaseTransferRenderer<
|
||||
onlyChildren={onlyChildren}
|
||||
value={selectedOptions}
|
||||
options={options}
|
||||
accumulatedOptions={formItem?.accumulatedOptions ?? []}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
option2value={this.option2value}
|
||||
@ -607,6 +659,28 @@ export class BaseTransferRenderer<
|
||||
showInvalidMatch={showInvalidMatch}
|
||||
mobileUI={mobileUI}
|
||||
noResultsText={noResultsText}
|
||||
pagination={{
|
||||
...pick(pagination, [
|
||||
'className',
|
||||
'layout',
|
||||
'perPageAvailable',
|
||||
'popOverContainerSelector'
|
||||
]),
|
||||
enable:
|
||||
!!formItem?.enableSourcePagination &&
|
||||
(!selectMode ||
|
||||
selectMode === 'list' ||
|
||||
selectMode === 'table') &&
|
||||
options.length > 0,
|
||||
maxButtons: Number.isInteger(pagination?.maxButtons)
|
||||
? pagination.maxButtons
|
||||
: 5,
|
||||
page: formItem?.sourcePageNum,
|
||||
perPage: formItem?.sourcePerPageNum,
|
||||
total: formItem?.sourceTotalNum,
|
||||
popOverContainer: popOverContainer ?? env?.getModalContainer
|
||||
}}
|
||||
onPageChange={this.handlePageChange}
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
|
@ -3,7 +3,13 @@ import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
CustomStyle,
|
||||
setThemeClassName
|
||||
setThemeClassName,
|
||||
ActionObject,
|
||||
IScopedContext,
|
||||
ScopedContext,
|
||||
createObject,
|
||||
resolveVariableAndFilter,
|
||||
isPureVariable
|
||||
} from 'amis-core';
|
||||
import {filter} from 'amis-core';
|
||||
import {themeable, ThemeProps} from 'amis-core';
|
||||
@ -398,6 +404,8 @@ export interface ImageFieldProps extends RendererProps {
|
||||
enlargeWithGallary?: boolean;
|
||||
showToolbar?: boolean;
|
||||
toolbarActions?: ImageAction[];
|
||||
maxScale?: number;
|
||||
minScale?: number;
|
||||
onImageEnlarge?: (
|
||||
info: {
|
||||
src: string;
|
||||
@ -414,9 +422,21 @@ export interface ImageFieldProps extends RendererProps {
|
||||
target: any
|
||||
) => void;
|
||||
imageGallaryClassName?: string;
|
||||
onClick?:
|
||||
| ((e: React.MouseEvent<any>, props: any) => void)
|
||||
| string
|
||||
| Function
|
||||
| null;
|
||||
}
|
||||
|
||||
export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
interface ImageFieldState {
|
||||
scale: number; // 放大倍率
|
||||
}
|
||||
|
||||
export class ImageField extends React.Component<
|
||||
ImageFieldProps,
|
||||
ImageFieldState
|
||||
> {
|
||||
static defaultProps: Pick<
|
||||
ImageFieldProps,
|
||||
'defaultImage' | 'thumbMode' | 'thumbRatio'
|
||||
@ -426,6 +446,10 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
thumbRatio: '1:1'
|
||||
};
|
||||
|
||||
state: ImageFieldState = {
|
||||
scale: 1
|
||||
};
|
||||
|
||||
@autobind
|
||||
handleEnlarge({
|
||||
src,
|
||||
@ -468,13 +492,73 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleClick(e: React.MouseEvent<HTMLElement>) {
|
||||
async handleClick(e: React.MouseEvent<HTMLElement>) {
|
||||
const {dispatchEvent, data} = this.props;
|
||||
const clickAction = this.props.clickAction;
|
||||
const rendererEvent = await dispatchEvent(
|
||||
e,
|
||||
createObject(data, {
|
||||
nativeEvent: e
|
||||
})
|
||||
);
|
||||
|
||||
if (rendererEvent?.prevented) {
|
||||
return;
|
||||
}
|
||||
if (clickAction) {
|
||||
handleAction(e, clickAction, this.props);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleMouseEnter(e: React.MouseEvent<any>) {
|
||||
const {dispatchEvent, data} = this.props;
|
||||
dispatchEvent(e, data);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleMouseLeave(e: React.MouseEvent<any>) {
|
||||
const {dispatchEvent, data} = this.props;
|
||||
dispatchEvent(e, data);
|
||||
}
|
||||
|
||||
handleSelfAction(actionType: string, action: ActionObject) {
|
||||
let {data, maxScale = 200, minScale = 50} = this.props;
|
||||
let {scale = 50} = action.args;
|
||||
if (actionType === 'zoom') {
|
||||
if (isPureVariable(maxScale)) {
|
||||
maxScale = isNaN(
|
||||
resolveVariableAndFilter(maxScale, createObject(action.data, data))
|
||||
)
|
||||
? 200
|
||||
: resolveVariableAndFilter(maxScale, createObject(action.data, data));
|
||||
}
|
||||
if (isPureVariable(minScale)) {
|
||||
minScale = isNaN(
|
||||
resolveVariableAndFilter(minScale, createObject(action.data, data))
|
||||
)
|
||||
? 50
|
||||
: resolveVariableAndFilter(minScale, createObject(action.data, data));
|
||||
}
|
||||
|
||||
if (scale >= 0) {
|
||||
this.setState({
|
||||
scale:
|
||||
this.state.scale + scale / 100 < maxScale / 100
|
||||
? this.state.scale + scale / 100
|
||||
: maxScale / 100
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
scale:
|
||||
this.state.scale + scale / 100 > minScale / 100
|
||||
? this.state.scale + scale / 100
|
||||
: minScale / 100
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
@ -510,6 +594,7 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
defaultImage && !value
|
||||
? filter(defaultImage, data, '| raw')
|
||||
: imagePlaceholder;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
@ -520,8 +605,10 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
className,
|
||||
setThemeClassName('wrapperCustomStyle', id, wrapperCustomStyle)
|
||||
)}
|
||||
style={style}
|
||||
style={{...style, transform: `scale(${this.state.scale})`}}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
{value || (!value && !placeholder) ? (
|
||||
<ThemedImageThumb
|
||||
@ -537,7 +624,7 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
thumbMode={thumbMode}
|
||||
thumbRatio={thumbRatio}
|
||||
originalSrc={filter(originalSrc, data, '| raw') ?? value}
|
||||
enlargeAble={enlargeAble && value !== defaultValue}
|
||||
enlargeAble={enlargeAble && value && value !== defaultValue}
|
||||
onEnlarge={this.handleEnlarge}
|
||||
imageMode={imageMode}
|
||||
imageControlClassName={setThemeClassName(
|
||||
@ -597,4 +684,26 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
@Renderer({
|
||||
type: 'image'
|
||||
})
|
||||
export class ImageFieldRenderer extends ImageField {}
|
||||
export class ImageFieldRenderer extends ImageField {
|
||||
static contextType = ScopedContext;
|
||||
constructor(props: ImageFieldProps, context: IScopedContext) {
|
||||
super(props);
|
||||
|
||||
const scoped = context;
|
||||
scoped.registerComponent(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const scoped = this.context as IScopedContext;
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
doAction(action: ActionObject) {
|
||||
const actionType = action?.actionType as string;
|
||||
if (actionType === 'preview') {
|
||||
this.handleEnlarge(this.props as ImageThumbProps);
|
||||
} else {
|
||||
this.handleSelfAction(actionType, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ import React from 'react';
|
||||
import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
autobind,
|
||||
createObject,
|
||||
isPureVariable,
|
||||
resolveEventData,
|
||||
resolveVariableAndFilter
|
||||
} from 'amis-core';
|
||||
import {BaseSchema} from '../Schema';
|
||||
@ -103,11 +106,31 @@ export default class Pagination extends React.Component<PaginationProps> {
|
||||
return result ?? defaultValue;
|
||||
}
|
||||
|
||||
@autobind
|
||||
async onPageChange(page: number, perPage?: number, dir?: string) {
|
||||
const {onPageChange, dispatchEvent, data} = this.props;
|
||||
|
||||
const rendererEvent = await dispatchEvent?.(
|
||||
'change',
|
||||
createObject(data, {
|
||||
page: page,
|
||||
perPage: perPage
|
||||
})
|
||||
);
|
||||
|
||||
if (rendererEvent?.prevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
onPageChange?.(page, perPage, dir);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {maxButtons, activePage, total, perPage} = this.props;
|
||||
return (
|
||||
<BasicPagination
|
||||
{...this.props}
|
||||
onPageChange={this.onPageChange}
|
||||
maxButtons={this.formatNumber(maxButtons)}
|
||||
activePage={this.formatNumber(activePage)}
|
||||
total={this.formatNumber(total)}
|
||||
|
@ -512,7 +512,7 @@ export const HocQuickEdit =
|
||||
>
|
||||
{render('quick-edit-form', this.buildSchema(), {
|
||||
value: undefined,
|
||||
static: false,
|
||||
defaultStatic: false,
|
||||
onSubmit: this.handleSubmit,
|
||||
onAction: this.handleAction,
|
||||
onChange: null,
|
||||
@ -577,7 +577,8 @@ export const HocQuickEdit =
|
||||
mode: 'normal',
|
||||
value: value ?? '',
|
||||
onChange: this.handleFormItemChange,
|
||||
ref: this.formItemRef
|
||||
ref: this.formItemRef,
|
||||
defaultStatic: false
|
||||
});
|
||||
}
|
||||
|
||||
@ -591,7 +592,8 @@ export const HocQuickEdit =
|
||||
onChange: this.handleChange,
|
||||
formLazyChange: false,
|
||||
canAccessSuperData,
|
||||
disabled
|
||||
disabled,
|
||||
defaultStatic: false
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ export default class SwitchContainer extends React.Component<
|
||||
|
||||
componentDidUpdate(preProps: SwitchContainerProps) {
|
||||
const items = this.props.items || [];
|
||||
if (this.state.activeIndex >= 0 && !items[this.state.activeIndex]) {
|
||||
if (this.state.activeIndex > 0 && !items[this.state.activeIndex]) {
|
||||
this.setState({
|
||||
activeIndex: 0
|
||||
});
|
||||
|
@ -71,7 +71,7 @@ export function AutoFilterForm({
|
||||
})
|
||||
);
|
||||
|
||||
let showExpander = searchableColumns.length >= columnsNum;
|
||||
let showExpander = activedSearchableColumns.length >= columnsNum;
|
||||
|
||||
// todo 以后做动画
|
||||
if (!searchFormExpanded && body.length) {
|
||||
@ -100,7 +100,7 @@ export function AutoFilterForm({
|
||||
}
|
||||
lastGroup.body.push({
|
||||
type: 'container',
|
||||
className: 'ButtonToolbar text-right block',
|
||||
className: 'AutoFilterToolbar',
|
||||
wrapperBody: false,
|
||||
body: [
|
||||
{
|
||||
@ -114,32 +114,31 @@ export function AutoFilterForm({
|
||||
visible: showBtnToolbar,
|
||||
buttons: searchableColumns.map(column => {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
label: false,
|
||||
className: cx('Table-searchableForm-checkbox'),
|
||||
inputClassName: cx('Table-searchableForm-checkbox-inner'),
|
||||
name: `${
|
||||
column.searchable.strategy === 'jsonql' ? '' : '__search_'
|
||||
}${column.searchable?.name ?? column.name}`,
|
||||
option: column.searchable?.label ?? column.label,
|
||||
/**
|
||||
* syncLocation开启后,参数值会从地址栏Query中二次同步到数据域中,其中布尔(boolean)类型的值被转化为字符串
|
||||
* eg:
|
||||
* true ==> "true"
|
||||
* false ==> "false"
|
||||
* 所以这里将真值和假值转化成字符串格式规避
|
||||
*/
|
||||
trueValue: '1',
|
||||
falseValue: '0',
|
||||
value: !!column.enableSearch ? '1' : '0',
|
||||
badge: {
|
||||
offset: [-10, 5],
|
||||
visibleOn: `${
|
||||
column.toggable && !column.toggled && column.enableSearch
|
||||
}`
|
||||
},
|
||||
onChange: (value: '1' | '0') =>
|
||||
onItemToggleExpanded?.(column, value === '1' ? true : false)
|
||||
children: ({render}: any) =>
|
||||
render(
|
||||
`column-search-toggler-${column.id}`,
|
||||
{
|
||||
type: 'checkbox',
|
||||
label: false,
|
||||
className: cx('Table-searchableForm-checkbox'),
|
||||
inputClassName: cx('Table-searchableForm-checkbox-inner'),
|
||||
name: `__whatever_name`,
|
||||
option: column.searchable?.label ?? column.label,
|
||||
badge: {
|
||||
offset: [-10, 5],
|
||||
visibleOn: `${
|
||||
column.toggable &&
|
||||
!column.toggled &&
|
||||
column.enableSearch
|
||||
}`
|
||||
}
|
||||
},
|
||||
{
|
||||
value: activedSearchableColumns.includes(column),
|
||||
onChange: (value: any) =>
|
||||
onItemToggleExpanded?.(column, value)
|
||||
}
|
||||
)
|
||||
};
|
||||
})
|
||||
},
|
||||
@ -147,34 +146,35 @@ export function AutoFilterForm({
|
||||
{
|
||||
type: 'submit',
|
||||
label: __('search'),
|
||||
size: 'sm',
|
||||
level: 'primary',
|
||||
className: 'w-18'
|
||||
className: 'w-18 mr-2'
|
||||
},
|
||||
{
|
||||
type: 'reset',
|
||||
label: __('reset'),
|
||||
size: 'sm',
|
||||
className: 'w-18'
|
||||
},
|
||||
|
||||
showExpander
|
||||
? {
|
||||
children: () => (
|
||||
<a
|
||||
className={cx(
|
||||
'Table-SFToggler',
|
||||
searchFormExpanded ? 'is-expanded' : ''
|
||||
)}
|
||||
onClick={onToggleExpanded}
|
||||
>
|
||||
{__(searchFormExpanded ? 'collapse' : 'expand')}
|
||||
<span className={cx('Table-SFToggler-arrow')}>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
: null
|
||||
].filter(item => item)
|
||||
{
|
||||
children: () =>
|
||||
showExpander ? (
|
||||
<a
|
||||
className={cx(
|
||||
'Table-SFToggler',
|
||||
searchFormExpanded ? 'is-expanded' : ''
|
||||
)}
|
||||
onClick={onToggleExpanded}
|
||||
>
|
||||
{__(searchFormExpanded ? 'collapse' : 'expand')}
|
||||
<span className={cx('Table-SFToggler-arrow')}>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</span>
|
||||
</a>
|
||||
) : null
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
@ -227,7 +227,7 @@ export default observer(
|
||||
const onItemToggleExpanded = React.useCallback(
|
||||
(column: IColumn, value: boolean) => {
|
||||
column.setEnableSearch(value);
|
||||
store.setSearchFormExpanded(true);
|
||||
value && store.setSearchFormExpanded(true);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
@ -115,7 +115,9 @@ export interface Action extends Button {
|
||||
| 'selectAll'
|
||||
| 'changeTabKey'
|
||||
| 'click'
|
||||
| 'stopAutoRefresh';
|
||||
| 'stopAutoRefresh'
|
||||
| 'preview'
|
||||
| 'zoom';
|
||||
api?: SchemaApi;
|
||||
asyncApi?: SchemaApi;
|
||||
payload?: any;
|
||||
|
Loading…
Reference in New Issue
Block a user