mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 11:58:10 +08:00
Merge branch 'baidu:master' into fix-transfer
This commit is contained in:
commit
f57de85e67
@ -63,6 +63,9 @@ npm run build
|
||||
# 执行测试用例
|
||||
npm test --workspaces
|
||||
|
||||
# 测试某个用例
|
||||
npm test --workspace amis inputImage
|
||||
|
||||
# 查看测试用例覆盖率
|
||||
npm run coverage
|
||||
|
||||
|
@ -179,6 +179,56 @@ fieldSet 的另一种标题展现样式,不同的是展开的时候收起文
|
||||
}
|
||||
```
|
||||
|
||||
## 嵌套使用
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "fieldSet",
|
||||
"title": "基本配置",
|
||||
"collapsable": true,
|
||||
"body": [
|
||||
{
|
||||
"name": "text1",
|
||||
"type": "input-text",
|
||||
"label": "文本1"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "text2",
|
||||
"type": "input-text",
|
||||
"label": "文本2"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "fieldSet",
|
||||
"title": "基本配置",
|
||||
"collapsable": true,
|
||||
"collapsed": true,
|
||||
size: 'base',
|
||||
"body": [
|
||||
{
|
||||
"name": "text1",
|
||||
"type": "input-text",
|
||||
"label": "文本1"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "text2",
|
||||
"type": "input-text",
|
||||
"label": "文本2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
@ -192,3 +242,4 @@ fieldSet 的另一种标题展现样式,不同的是展开的时候收起文
|
||||
| collapsable | `boolean` | `false` | 是否可折叠 |
|
||||
| collapsed | `booelan` | `false` | 默认是否折叠 |
|
||||
| collapseTitle | [SchemaNode](../../../docs/types/schemanode) | `收起` | 收起的标题 |
|
||||
| size | string | `` | 大小,支持 xs、sm、base、lg、xl |
|
||||
|
@ -237,7 +237,7 @@ app.listen(8080, function () {});
|
||||
}
|
||||
```
|
||||
|
||||
**多选模式**
|
||||
### 多选模式
|
||||
|
||||
当表单项为多选模式时,不能再直接取选项中的值了,而是通过 `items` 变量来取,通过它可以获取当前选中的选项集合。
|
||||
|
||||
@ -257,12 +257,75 @@ app.listen(8080, function () {});
|
||||
"myUrl": "${items|pick:url}",
|
||||
"lastUrl": "${items|last|pick:url}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tpl",
|
||||
"label": false,
|
||||
"inline": false,
|
||||
"tpl": "<strong>myUrl集合</strong>"
|
||||
},
|
||||
{
|
||||
"type": "each",
|
||||
"name": "myUrl",
|
||||
"className": "mb-1",
|
||||
"items": {
|
||||
"type": "tpl",
|
||||
"tpl": "<span class='label label-info m-l-sm inline-block mb-1'><%= data.item %></span>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tpl",
|
||||
"label": false,
|
||||
"inline": false,
|
||||
"tpl": "<strong>lastUrl</strong>"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"name": "lastUrl",
|
||||
"label": "lastUrl",
|
||||
"inline": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**initAutoFill**
|
||||
### 其他表单项填充
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"title": "表单",
|
||||
"body": [
|
||||
{
|
||||
"type": "select",
|
||||
"label": "选项",
|
||||
"name": "imageUrl",
|
||||
"delimiter": "|",
|
||||
"autoFill": {
|
||||
"inputImage": "${value}"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"label": "imageURL",
|
||||
"value": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
},
|
||||
{
|
||||
"label": "空链接",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "input-image",
|
||||
"label": "图片上传",
|
||||
"name": "inputImage",
|
||||
"imageClassName": "r w-full"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### initAutoFill 初始化时自动同步
|
||||
|
||||
当表单反显时,可通过`initAutoFill`控制`autoFill`在数据反显时是否执行。
|
||||
|
||||
|
@ -115,6 +115,28 @@ order: 32
|
||||
}
|
||||
```
|
||||
|
||||
## 是否是大数
|
||||
|
||||
> 2.3.0 及以上版本
|
||||
|
||||
默认情况下使用 JavaScript 原生数字类型,但如果要支持输入超过 JavaScript 支持范围的整数或浮点数,可以通过 `"big": true` 开启大数支持,开启之后输入输出都将是字符串
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"debug": true,
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-number",
|
||||
"name": "number",
|
||||
"label": "数字",
|
||||
"big": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 原生数字组件
|
||||
|
||||
原生数字组件将直接使用浏览器的实现,最终展现效果和浏览器有关,而且只支持 `min`、`max`、`step` 这几个属性设置。
|
||||
@ -148,6 +170,7 @@ order: 32
|
||||
| suffix | `string` | | 后缀 |
|
||||
| kilobitSeparator | `boolean` | | 千分分隔 |
|
||||
| keyboard | `boolean` | | 键盘事件(方向上下) |
|
||||
| big | `boolean` | | 是否使用大数 |
|
||||
| displayMode | `string` | | 样式类型 |
|
||||
|
||||
## 事件表
|
||||
|
@ -1457,14 +1457,17 @@ order: 2
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"debug": true,
|
||||
"debugConfig": {
|
||||
"levelExpand": 2
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "select",
|
||||
"label": "选项",
|
||||
"label": "autoFill触发器",
|
||||
"name": "select",
|
||||
"autoFill": {
|
||||
"option.instantValidate": "${label}",
|
||||
"option.submitValidate": "${label}",
|
||||
"option.submitValidate": "${label}"
|
||||
},
|
||||
"clearable": true,
|
||||
"options": [
|
||||
@ -1481,7 +1484,7 @@ order: 2
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "option.instantValidate",
|
||||
"label": "选中项",
|
||||
"label": "目标1",
|
||||
"description": "填充后立即校验",
|
||||
"required": true,
|
||||
"validateOnChange": true,
|
||||
@ -1495,7 +1498,7 @@ order: 2
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "option.submitValidate",
|
||||
"label": "选中项1",
|
||||
"label": "目标2",
|
||||
"description": "填充后提交表单时才校验",
|
||||
"required": true,
|
||||
"validations": {
|
||||
@ -1504,6 +1507,13 @@ order: 2
|
||||
"validationErrors": {
|
||||
"equals": "校验失败,数据必须为Option B"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "option.c",
|
||||
"label": "表单项3",
|
||||
"description":'不受autoFill影响的表单项',
|
||||
"value": "abc",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -460,6 +460,12 @@ order: 67
|
||||
|
||||
## 可展开
|
||||
|
||||
支持点击按钮展开/关闭当前行的自定义内容,展开按钮可放在表格的最左侧、最右侧或通过事件动作来触发展开。
|
||||
|
||||
### 默认展开
|
||||
|
||||
默认模式 展开按钮在表格最左侧
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
@ -490,7 +496,7 @@ order: 67
|
||||
}
|
||||
],
|
||||
"expandable": {
|
||||
"expandableOn": "this.record.id === 1 || this.record.id === 3",
|
||||
"expandableOn": "this.record && (this.record.id === 1 || this.record.id === 3)",
|
||||
"keyField": "id",
|
||||
"expandedRowClassNameExpr": "<%= data.rowIndex % 2 ? 'bg-success' : '' %>",
|
||||
"expandedRowKeys": ["3"],
|
||||
@ -507,7 +513,7 @@ order: 67
|
||||
}
|
||||
```
|
||||
|
||||
## 已展开 - 正则表达式
|
||||
### 默认展开 - 正则表达式
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
@ -539,7 +545,7 @@ order: 67
|
||||
}
|
||||
],
|
||||
"expandable": {
|
||||
"expandableOn": "this.record.id === 1 || this.record.id === 3",
|
||||
"expandableOn": "this.record && (this.record.id === 1 || this.record.id === 3)",
|
||||
"keyField": "id",
|
||||
"expandedRowClassNameExpr": "<%= data.rowIndex % 2 ? 'bg-success' : '' %>",
|
||||
"expandedRowKeysExpr": "data.record.id == '3'",
|
||||
@ -556,6 +562,207 @@ order: 67
|
||||
}
|
||||
```
|
||||
|
||||
### 右侧展开按钮
|
||||
|
||||
通过设置`expandable.position`属性为`right`控制,支持 不设置、`left`、`right`、`none`四种情况。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
"api": "/api/sample?perPage=5",
|
||||
"body": [
|
||||
{
|
||||
"type": "table2",
|
||||
"source": "$rows",
|
||||
"columns": [
|
||||
{
|
||||
"title": "Engine",
|
||||
"name": "engine"
|
||||
},
|
||||
{
|
||||
"title": "Version",
|
||||
"name": "version"
|
||||
},
|
||||
{
|
||||
"title": "Browser",
|
||||
"name": "browser"
|
||||
},
|
||||
{
|
||||
"title": "Operation",
|
||||
"name": "operation",
|
||||
"type": "button",
|
||||
"label": "删除",
|
||||
"size": "sm"
|
||||
}
|
||||
],
|
||||
"expandable": {
|
||||
"expandableOn": "this.record && (this.record.id === 1 || this.record.id === 3)",
|
||||
"keyField": "id",
|
||||
"expandedRowClassNameExpr": "<%= data.rowIndex % 2 ? 'bg-success' : '' %>",
|
||||
"expandedRowKeys": ["3"],
|
||||
"type": "container",
|
||||
"position": "right",
|
||||
"body": [
|
||||
{
|
||||
"type": "tpl",
|
||||
"html": "<div class=\"test\">测试测试</div>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"footSummary": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "总计"
|
||||
},
|
||||
{
|
||||
"type": "tpl",
|
||||
"tpl": "测试测试",
|
||||
"colSpan": 2
|
||||
},
|
||||
{
|
||||
"type": "tpl",
|
||||
"tpl": "最后一列"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 无展开按钮
|
||||
|
||||
可设置无展开按钮,通过事件动作展开关闭,可单独行控制
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
"api": "/api/sample?perPage=5",
|
||||
"body": [
|
||||
{
|
||||
"type": "table2",
|
||||
"source": "$rows",
|
||||
"id": "table-select",
|
||||
"columns": [
|
||||
{
|
||||
"title": "Engine",
|
||||
"name": "engine"
|
||||
},
|
||||
{
|
||||
"title": "Version",
|
||||
"name": "version"
|
||||
},
|
||||
{
|
||||
"title": "Browser",
|
||||
"name": "browser"
|
||||
},
|
||||
{
|
||||
"title": "Operation",
|
||||
"name": "operation",
|
||||
"type": "button",
|
||||
"label": "展开",
|
||||
"size": "sm",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "expand",
|
||||
"componentId": "table-select",
|
||||
"description": "展开行",
|
||||
"args": {
|
||||
"value": "${id}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"expandable": {
|
||||
"keyField": "id",
|
||||
"expandedRowClassNameExpr": "<%= data.rowIndex % 2 ? 'bg-success' : '' %>",
|
||||
"type": "container",
|
||||
"position": "none",
|
||||
"body": [
|
||||
{
|
||||
"type": "tpl",
|
||||
"html": "<div class=\"test\">测试测试</div>"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
也可以通过正则表达式一次控制多行展开关闭
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "service",
|
||||
"api": "/api/sample?perPage=5",
|
||||
"body": [
|
||||
{
|
||||
"type": "container",
|
||||
"style": {
|
||||
"marginBottom": "5px"
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "button",
|
||||
"label": "展开",
|
||||
"size": "sm",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "expand",
|
||||
"componentId": "table-select2",
|
||||
"description": "展开行",
|
||||
"args": {
|
||||
"expandedRowsExpr": "data.record?.id === 1 || data.record?.id === 3"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "table2",
|
||||
"source": "$rows",
|
||||
"id": "table-select2",
|
||||
"columns": [
|
||||
{
|
||||
"title": "Engine",
|
||||
"name": "engine"
|
||||
},
|
||||
{
|
||||
"title": "Version",
|
||||
"name": "version"
|
||||
},
|
||||
{
|
||||
"title": "Browser",
|
||||
"name": "browser"
|
||||
}
|
||||
],
|
||||
"expandable": {
|
||||
"keyField": "id",
|
||||
"expandedRowClassNameExpr": "<%= data.rowIndex % 2 ? 'bg-success' : '' %>",
|
||||
"type": "container",
|
||||
"position": "none",
|
||||
"body": [
|
||||
{
|
||||
"type": "tpl",
|
||||
"html": "<div class=\"test\">测试测试</div>"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 表格行/列合并
|
||||
|
||||
```schema: scope="body"
|
||||
|
@ -89,7 +89,8 @@ export default {
|
||||
{
|
||||
title: '超级小',
|
||||
type: 'fieldSet',
|
||||
className: 'fieldset-xs',
|
||||
collapsable: true,
|
||||
size: 'xs',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
@ -100,7 +101,8 @@ export default {
|
||||
{
|
||||
title: '小尺寸',
|
||||
type: 'fieldSet',
|
||||
className: 'fieldset-sm',
|
||||
collapsable: true,
|
||||
size: 'sm',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
@ -111,7 +113,8 @@ export default {
|
||||
{
|
||||
title: '正常尺寸',
|
||||
type: 'fieldSet',
|
||||
className: 'fieldset',
|
||||
collapsable: true,
|
||||
size: 'base',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
@ -122,7 +125,8 @@ export default {
|
||||
{
|
||||
title: '中大尺寸',
|
||||
type: 'fieldSet',
|
||||
className: 'fieldset-md',
|
||||
collapsable: true,
|
||||
size: 'md',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
@ -133,7 +137,8 @@ export default {
|
||||
{
|
||||
title: '超大尺寸',
|
||||
type: 'fieldSet',
|
||||
className: 'fieldset-lg',
|
||||
collapsable: true,
|
||||
size: 'lg',
|
||||
body: [
|
||||
{
|
||||
type: 'plain',
|
||||
|
@ -2,5 +2,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "2.2.0"
|
||||
"version": "2.3.0"
|
||||
}
|
@ -85,6 +85,10 @@
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"/.rollup.cache/"
|
||||
]
|
||||
],
|
||||
"snapshotFormat": {
|
||||
"escapeString": false,
|
||||
"printBasicPrototype": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amis-core",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"description": "amis-core",
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
@ -43,7 +43,7 @@
|
||||
"esm"
|
||||
],
|
||||
"dependencies": {
|
||||
"amis-formula": "^2.1.0",
|
||||
"amis-formula": "^2.3.0",
|
||||
"classnames": "2.3.1",
|
||||
"file-saver": "^2.0.2",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
|
@ -25,7 +25,7 @@ export interface ScopedComponentType extends React.Component<RendererProps> {
|
||||
data: RendererData,
|
||||
throwErrors?: boolean
|
||||
) => void;
|
||||
receive?: (values: RendererData, subPath?: string) => void;
|
||||
receive?: (values: RendererData, subPath?: string, replace?: boolean) => void;
|
||||
reload?: (
|
||||
subPath?: string,
|
||||
query?: RendererData | null,
|
||||
|
@ -21,7 +21,8 @@ export interface ListenerAction {
|
||||
actionType: string; // 动作类型 逻辑动作|自定义(脚本支撑)|reload|url|ajax|dialog|drawer 其他扩充的组件动作
|
||||
description?: string; // 事件描述,actionType: broadcast
|
||||
componentId?: string; // 组件ID,用于直接执行指定组件的动作
|
||||
args?: Record<string, any>; // 参数,可以配置数据映射
|
||||
args?: Record<string, any> | null; // 参数,可以配置数据映射
|
||||
dataMergeMode?: 'merge' | 'override'; // 参数模式,合并或者覆盖
|
||||
outputVar?: string; // 输出数据变量名
|
||||
preventDefault?: boolean; // 阻止原有组件的动作行为
|
||||
stopPropagation?: boolean; // 阻止后续的事件处理器执行
|
||||
@ -145,14 +146,13 @@ export const runAction = async (
|
||||
actionConfig.stopPropagation &&
|
||||
evalExpression(String(actionConfig.stopPropagation), mergeData);
|
||||
|
||||
// 修正参数,处理数据映射
|
||||
let args = event.data;
|
||||
|
||||
if (actionConfig.args) {
|
||||
args = dataMapping(actionConfig.args, mergeData, key =>
|
||||
['adaptor', 'responseAdaptor', 'requestAdaptor'].includes(key)
|
||||
);
|
||||
}
|
||||
// 处理数据映射,默认参数为事件数据
|
||||
let args =
|
||||
actionConfig.args !== undefined
|
||||
? dataMapping(actionConfig.args, mergeData, key =>
|
||||
['adaptor', 'responseAdaptor', 'requestAdaptor'].includes(key)
|
||||
)
|
||||
: event.data;
|
||||
|
||||
await actionInstrance.run(
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ export class BroadcastAction implements RendererAction {
|
||||
}
|
||||
|
||||
// 作为一个新的事件,需要把广播动作的args参数追加到事件数据中
|
||||
event.setData(createObject(event.data, action.args));
|
||||
event.setData(createObject(event.data, action.args ?? {}));
|
||||
|
||||
// 直接触发对应的动作
|
||||
return await dispatchEvent(
|
||||
|
@ -41,6 +41,7 @@ export class CmptAction implements RendererAction {
|
||||
action.componentId && renderer.props.$schema.id !== action.componentId
|
||||
? event.context.scoped?.getComponentById(action.componentId)
|
||||
: renderer;
|
||||
const dataMergeMode = action.dataMergeMode || 'merge';
|
||||
|
||||
// 显隐&状态控制
|
||||
if (['show', 'hidden'].includes(action.actionType)) {
|
||||
@ -58,7 +59,11 @@ export class CmptAction implements RendererAction {
|
||||
// 数据更新
|
||||
if (action.actionType === 'setValue') {
|
||||
if (component?.setData) {
|
||||
return component?.setData(action.args?.value, action.args?.index);
|
||||
return component?.setData(
|
||||
action.args?.value,
|
||||
dataMergeMode === 'override',
|
||||
action.args?.index
|
||||
);
|
||||
} else {
|
||||
return component?.props.onChange?.(action.args?.value);
|
||||
}
|
||||
@ -66,7 +71,13 @@ export class CmptAction implements RendererAction {
|
||||
|
||||
// 刷新
|
||||
if (action.actionType === 'reload') {
|
||||
return component?.reload?.(undefined, action.args);
|
||||
return component?.reload?.(
|
||||
undefined,
|
||||
action.args,
|
||||
undefined,
|
||||
undefined,
|
||||
dataMergeMode === 'override'
|
||||
);
|
||||
}
|
||||
|
||||
// 执行组件动作
|
||||
|
@ -755,10 +755,10 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
: store.reset(undefined, false);
|
||||
}
|
||||
|
||||
receive(values: object) {
|
||||
receive(values: object, name?: string, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
|
||||
store.updateData(values);
|
||||
store.updateData(values, undefined, replace);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@ -812,10 +812,10 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
return store.data;
|
||||
}
|
||||
|
||||
setValues(value: any) {
|
||||
setValues(value: any, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
this.flush();
|
||||
store.setValues(value);
|
||||
store.setValues(value, undefined, replace);
|
||||
}
|
||||
|
||||
submit(fn?: (values: object) => Promise<any>): Promise<any> {
|
||||
@ -1872,9 +1872,15 @@ export class FormRenderer extends Form {
|
||||
scoped.close(target);
|
||||
}
|
||||
|
||||
reload(target?: string, query?: any, ctx?: any, silent?: boolean) {
|
||||
reload(
|
||||
target?: string,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
}
|
||||
|
||||
const scoped = this.context as IScopedContext;
|
||||
@ -1913,7 +1919,7 @@ export class FormRenderer extends Form {
|
||||
}
|
||||
}
|
||||
|
||||
receive(values: object, name?: string) {
|
||||
receive(values: object, name?: string, replace?: boolean) {
|
||||
if (name) {
|
||||
const scoped = this.context as IScopedContext;
|
||||
const idx = name.indexOf('.');
|
||||
@ -1929,10 +1935,10 @@ export class FormRenderer extends Form {
|
||||
return;
|
||||
}
|
||||
|
||||
return super.receive(values);
|
||||
return super.receive(values, undefined, replace);
|
||||
}
|
||||
|
||||
setData(values: object) {
|
||||
setData(values: object, replace?: boolean) {
|
||||
return super.setValues(values);
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,7 @@ import {
|
||||
getTreeDepth,
|
||||
flattenTree,
|
||||
keyToPath,
|
||||
getVariable,
|
||||
isObject
|
||||
getVariable
|
||||
} from '../utils/helper';
|
||||
import {reaction} from 'mobx';
|
||||
import {
|
||||
@ -543,6 +542,8 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
selectedOptions[0]
|
||||
)
|
||||
);
|
||||
const tmpData = {...data};
|
||||
const result = {...toSync};
|
||||
|
||||
Object.keys(autoFill).forEach(key => {
|
||||
const keys = keyToPath(key);
|
||||
@ -550,15 +551,16 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||
// 如果左边的 key 是一个路径
|
||||
// 这里不希望直接把原始对象都给覆盖没了
|
||||
// 而是保留原始的对象,只修改指定的属性
|
||||
if (keys.length > 1 && isPlainObject(data[keys[0]])) {
|
||||
const obj = {...data[keys[0]]};
|
||||
if (keys.length > 1 && isPlainObject(tmpData[keys[0]])) {
|
||||
const value = getVariable(toSync, key);
|
||||
toSync[keys[0]] = obj;
|
||||
setVariable(toSync, key, value);
|
||||
|
||||
// 存在情况:依次更新同一子路径的多个key,eg: a.b.c1 和 a.b.c2,所以需要同步更新data
|
||||
setVariable(tmpData, key, value);
|
||||
result[keys[0]] = tmpData[keys[0]];
|
||||
}
|
||||
});
|
||||
|
||||
onBulkChange(toSync);
|
||||
onBulkChange(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// 主要用于解决 0.1+0.2 结果的精度问题导致太长
|
||||
export function stripNumber(number: number) {
|
||||
if (typeof number === 'number' && !Number.isInteger(number)) {
|
||||
return parseFloat(number.toPrecision(12));
|
||||
return parseFloat(number.toPrecision(16));
|
||||
} else {
|
||||
return number;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {parse} from '../src/index';
|
||||
|
||||
test('parser:simple', () => {
|
||||
expect(
|
||||
parse('expression result is ${a + b}', {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amis-formula",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"description": "负责 amis 里面的表达式实现,内置公式,编辑器等",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
@ -94,7 +94,11 @@
|
||||
},
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/__tests__/jest.setup.js"
|
||||
]
|
||||
],
|
||||
"snapshotFormat": {
|
||||
"escapeString": false,
|
||||
"printBasicPrototype": false
|
||||
}
|
||||
},
|
||||
"gitHead": "37d23b4a8eb1c663bc38e8dd9040889ea1526ec4"
|
||||
}
|
||||
|
@ -1915,7 +1915,7 @@ function parseJson(str: string, defaultValue?: any) {
|
||||
|
||||
function stripNumber(number: number) {
|
||||
if (typeof number === 'number' && !Number.isInteger(number)) {
|
||||
return parseFloat(number.toPrecision(12));
|
||||
return parseFloat(number.toPrecision(16));
|
||||
} else {
|
||||
return number;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "npm run clean-dist && NODE_ENV=production rollup -c ",
|
||||
@ -33,8 +33,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"amis-core": "^2.2.0",
|
||||
"amis-formula": "^2.1.0",
|
||||
"amis-core": "^2.3.0",
|
||||
"amis-formula": "^2.3.0",
|
||||
"classnames": "2.3.1",
|
||||
"codemirror": "^5.63.0",
|
||||
"downshift": "6.1.12",
|
||||
|
@ -61,15 +61,15 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.is-active &-arrow:before {
|
||||
&.is-active > * > &-arrow:before {
|
||||
transform: rotate(135deg);
|
||||
transform-origin: 50% 30%;
|
||||
}
|
||||
&.is-active &-icon-tranform {
|
||||
&.is-active > * > &-icon-tranform {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&--disabled &-header {
|
||||
&--disabled > &-header {
|
||||
cursor: not-allowed;
|
||||
user-select: none;
|
||||
color: var(--text--muted-color);
|
||||
@ -78,7 +78,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--disabled &-arrow:before {
|
||||
&--disabled > * > &-arrow:before {
|
||||
border-color: var(--text--muted-color);
|
||||
}
|
||||
|
||||
|
@ -174,3 +174,9 @@
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Spinner-mark {
|
||||
position: absolute;
|
||||
z-index: -999;
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -61,6 +61,10 @@ fieldset.#{$ns}Collapse {
|
||||
font-size: var(--fontSizeXs);
|
||||
padding: 0 3px;
|
||||
margin: 0 0 0 -3px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
@ -78,6 +82,10 @@ fieldset.#{$ns}Collapse {
|
||||
font-size: var(--fontSizeSm);
|
||||
padding: 0 5px;
|
||||
margin: 0 0 0 -5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
@ -95,6 +103,10 @@ fieldset.#{$ns}Collapse {
|
||||
font-size: var(--fontSizeBase);
|
||||
padding: 0 8px;
|
||||
margin: 0 0 0 -8px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
@ -111,6 +123,10 @@ fieldset.#{$ns}Collapse {
|
||||
font-size: var(--fontSizeMd);
|
||||
padding: 0 10px;
|
||||
margin: 0 0 0 -10px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
@ -127,6 +143,10 @@ fieldset.#{$ns}Collapse {
|
||||
font-size: var(--fontSizeLg);
|
||||
padding: 0 var(--gap-md);
|
||||
margin: 0 0 0 calc(var(--gap-md) * -1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
|
@ -51,6 +51,11 @@ export interface NumberProps extends ThemeProps {
|
||||
*/
|
||||
displayMode?: 'base' | 'enhance';
|
||||
keyboard?: Boolean;
|
||||
|
||||
/**
|
||||
* 是否是大数
|
||||
*/
|
||||
big?: boolean;
|
||||
}
|
||||
|
||||
export class NumberInput extends React.Component<NumberProps, any> {
|
||||
@ -60,6 +65,19 @@ export class NumberInput extends React.Component<NumberProps, any> {
|
||||
borderMode: 'full'
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否是 bigNumber,如果输入的内容是字符串就自动开启
|
||||
*/
|
||||
isBig: boolean = false;
|
||||
|
||||
constructor(props: NumberProps) {
|
||||
super(props);
|
||||
const value = props.value;
|
||||
if (typeof value === 'string' || props.big) {
|
||||
this.isBig = true;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleChange(value: any) {
|
||||
const {min, max, onChange} = this.props;
|
||||
@ -74,6 +92,22 @@ export class NumberInput extends React.Component<NumberProps, any> {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
let val = getMiniDecimal(value);
|
||||
if (typeof min !== 'undefined') {
|
||||
let minValue = getMiniDecimal(min);
|
||||
if (val.lessEquals(minValue)) {
|
||||
value = min;
|
||||
}
|
||||
}
|
||||
if (typeof max !== 'undefined') {
|
||||
let maxValue = getMiniDecimal(max);
|
||||
if (maxValue.lessEquals(val)) {
|
||||
value = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChange?.(value);
|
||||
}
|
||||
@autobind
|
||||
@ -87,18 +121,19 @@ export class NumberInput extends React.Component<NumberProps, any> {
|
||||
const {onBlur} = this.props;
|
||||
onBlur && onBlur(e);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleEnhanceModeChange(action: 'add' | 'subtract'): void {
|
||||
const {value, step, disabled, readOnly, precision} = this.props;
|
||||
const {value, step = 1, disabled, readOnly, precision} = this.props;
|
||||
// value为undefined会导致溢出错误
|
||||
let val = Number(value) || 0;
|
||||
let val = value || 0;
|
||||
if (disabled || readOnly) {
|
||||
return;
|
||||
}
|
||||
if (isNaN(Number(step)) || !Number(step)) {
|
||||
return;
|
||||
}
|
||||
let stepDecimal = getMiniDecimal(Number(step));
|
||||
let stepDecimal = getMiniDecimal(step);
|
||||
if (action !== 'add') {
|
||||
stepDecimal = stepDecimal.negate();
|
||||
}
|
||||
@ -126,9 +161,14 @@ export class NumberInput extends React.Component<NumberProps, any> {
|
||||
return updateValue;
|
||||
};
|
||||
const updatedValue = triggerValueUpdate(target, false);
|
||||
val = Number(updatedValue.toString());
|
||||
this.handleChange(val);
|
||||
if (this.isBig) {
|
||||
this.handleChange(updatedValue.toString());
|
||||
} else {
|
||||
val = Number(updatedValue.toString());
|
||||
this.handleChange(val);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
renderBase() {
|
||||
const {
|
||||
@ -182,6 +222,7 @@ export class NumberInput extends React.Component<NumberProps, any> {
|
||||
placeholder={placeholder}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
stringMode={this.isBig ? true : false}
|
||||
keyboard={keyboard}
|
||||
{...precisionProps}
|
||||
/>
|
||||
|
@ -133,7 +133,8 @@ class HandleItem extends React.Component<HandleItemProps, HandleItemState> {
|
||||
this.setState({
|
||||
isDrag: false
|
||||
});
|
||||
this.props.onAfterChange();
|
||||
const {onAfterChange} = this.props;
|
||||
onAfterChange && onAfterChange();
|
||||
window.removeEventListener('mousemove', this.onMouseMove);
|
||||
window.removeEventListener('mouseup', this.onMouseUp);
|
||||
}
|
||||
@ -291,7 +292,8 @@ export class Range extends React.Component<RangeItemProps, any> {
|
||||
*/
|
||||
@autobind
|
||||
updateValue(value: FormatValue) {
|
||||
this.props.updateValue(value);
|
||||
const {onChange} = this.props;
|
||||
onChange && onChange(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +105,8 @@ export class Spinner extends React.Component<SpinnerProps> {
|
||||
};
|
||||
|
||||
state = {
|
||||
spinning: false
|
||||
spinning: false,
|
||||
showMark: true
|
||||
};
|
||||
|
||||
parent: HTMLElement | null = null;
|
||||
@ -113,15 +114,13 @@ export class Spinner extends React.Component<SpinnerProps> {
|
||||
spinnerRef = (dom: HTMLElement) => {
|
||||
if (dom) {
|
||||
this.parent = dom.parentNode as HTMLElement;
|
||||
this.setState({
|
||||
showMark: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate(prev: SpinnerProps) {
|
||||
if (!prev.show && this.props.show) {
|
||||
// 先根据 props.show 触发一次 loading,否则元素没有渲染,无法找到 parent
|
||||
this.setState({spinning: true});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.parent) {
|
||||
if (this.props.show) {
|
||||
store.push(this.parent);
|
||||
@ -131,18 +130,6 @@ export class Spinner extends React.Component<SpinnerProps> {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
// 对于 通过 条件语句控制 Spinner 是否展示的情况,需要在这里处理 : show && <Spinner show overlay />
|
||||
if (this.props.show) {
|
||||
// 先根据 props.show 触发一次 loading,否则元素没有渲染,无法找到 parent
|
||||
this.setState({spinning: true});
|
||||
|
||||
if (this.parent) {
|
||||
store.push(this.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// 卸载 reaction
|
||||
this.loadingChecker();
|
||||
@ -150,6 +137,9 @@ export class Spinner extends React.Component<SpinnerProps> {
|
||||
store.remove(this.parent!);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监控着 spinnerContainers 的变化
|
||||
*/
|
||||
loadingChecker = reaction(
|
||||
() => store.spinnerContainers.size,
|
||||
() => {
|
||||
@ -175,67 +165,71 @@ export class Spinner extends React.Component<SpinnerProps> {
|
||||
const timeout = {enter: delay, exit: 0};
|
||||
|
||||
return (
|
||||
<Transition
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
in={this.state.spinning}
|
||||
timeout={timeout}
|
||||
>
|
||||
{(status: string) => {
|
||||
return (
|
||||
<>
|
||||
{/* 遮罩层 */}
|
||||
{overlay ? (
|
||||
<div className={cx(`Spinner-overlay`, fadeStyles[status])} />
|
||||
) : null}
|
||||
<>
|
||||
{this.state.showMark && (
|
||||
<span className={cx('Spinner-mark')} ref={this.spinnerRef as any} />
|
||||
)}
|
||||
<Transition
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
in={this.state.spinning}
|
||||
timeout={timeout}
|
||||
>
|
||||
{(status: string) => {
|
||||
return (
|
||||
<>
|
||||
{/* 遮罩层 */}
|
||||
{overlay ? (
|
||||
<div className={cx(`Spinner-overlay`, fadeStyles[status])} />
|
||||
) : null}
|
||||
|
||||
{/* spinner图标和文案 */}
|
||||
<div
|
||||
ref={this.spinnerRef as any}
|
||||
data-testid="spinner"
|
||||
className={cx(
|
||||
`Spinner`,
|
||||
tip && {
|
||||
[`Spinner-tip--${tipPlacement}`]: [
|
||||
'top',
|
||||
'right',
|
||||
'bottom',
|
||||
'left'
|
||||
].includes(tipPlacement)
|
||||
},
|
||||
{[`Spinner--overlay`]: overlay},
|
||||
fadeStyles[status],
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* spinner图标和文案 */}
|
||||
<div
|
||||
data-testid="spinner"
|
||||
className={cx(
|
||||
`Spinner-icon`,
|
||||
{
|
||||
[`Spinner-icon--${size}`]: ['lg', 'sm'].includes(size),
|
||||
[`Spinner-icon--default`]: !icon,
|
||||
[`Spinner-icon--simple`]: !isCustomIcon && icon,
|
||||
[`Spinner-icon--custom`]: isCustomIcon
|
||||
`Spinner`,
|
||||
tip && {
|
||||
[`Spinner-tip--${tipPlacement}`]: [
|
||||
'top',
|
||||
'right',
|
||||
'bottom',
|
||||
'left'
|
||||
].includes(tipPlacement)
|
||||
},
|
||||
spinnerClassName
|
||||
{[`Spinner--overlay`]: overlay},
|
||||
fadeStyles[status],
|
||||
className
|
||||
)}
|
||||
>
|
||||
{icon ? (
|
||||
isCustomIcon ? (
|
||||
icon
|
||||
) : hasIcon(icon as string) ? (
|
||||
<Icon icon={icon} className="icon" />
|
||||
) : (
|
||||
generateIcon(cx, icon as string, 'icon')
|
||||
)
|
||||
) : null}
|
||||
<div
|
||||
className={cx(
|
||||
`Spinner-icon`,
|
||||
{
|
||||
[`Spinner-icon--${size}`]: ['lg', 'sm'].includes(size),
|
||||
[`Spinner-icon--default`]: !icon,
|
||||
[`Spinner-icon--simple`]: !isCustomIcon && icon,
|
||||
[`Spinner-icon--custom`]: isCustomIcon
|
||||
},
|
||||
spinnerClassName
|
||||
)}
|
||||
>
|
||||
{icon ? (
|
||||
isCustomIcon ? (
|
||||
icon
|
||||
) : hasIcon(icon as string) ? (
|
||||
<Icon icon={icon} className="icon" />
|
||||
) : (
|
||||
generateIcon(cx, icon as string, 'icon')
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
{tip ? <span className={cx(`Spinner-tip`)}>{tip}</span> : ''}
|
||||
</div>
|
||||
{tip ? <span className={cx(`Spinner-tip`)}>{tip}</span> : ''}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ export interface ExpandableProps {
|
||||
expandedRowClassName?: Function;
|
||||
expandIcon?: Function;
|
||||
fixed?: boolean;
|
||||
position?: string; // 控制展开按钮的位置 不设置默认是在左侧 设置支持left、right、none
|
||||
}
|
||||
|
||||
export interface SummaryProps {
|
||||
@ -154,6 +155,7 @@ export interface TableProps extends ThemeProps, LocaleProps {
|
||||
onSelect?: Function;
|
||||
onSelectAll?: Function;
|
||||
itemActions?: Function;
|
||||
onRef?: (ref: any) => void;
|
||||
}
|
||||
|
||||
export interface ScrollProps {
|
||||
@ -463,6 +465,8 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props?.onRef?.(this);
|
||||
|
||||
if (this.props.loading) {
|
||||
return;
|
||||
}
|
||||
@ -792,7 +796,20 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
|
||||
const tdColumns = this.tdColumns;
|
||||
const isExpandable = this.isExpandableTable();
|
||||
const extraCount = this.getExtraColumnCount();
|
||||
const extraCount =
|
||||
this.getExtraColumnCount() - (this.isRightExpandable() ? 1 : 0);
|
||||
const isLeftExpandable = this.isLeftExpandable();
|
||||
const isRightExpandable = this.isRightExpandable();
|
||||
|
||||
const expandableCol =
|
||||
!draggable && isExpandable ? (
|
||||
<col
|
||||
className={cx('Table-expand-col')}
|
||||
style={{
|
||||
width: (expandable?.columnWidth || DefaultCellWidth) + 'px'
|
||||
}}
|
||||
></col>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<colgroup>
|
||||
@ -810,16 +827,11 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
}}
|
||||
></col>
|
||||
) : null}
|
||||
{!draggable && isExpandable ? (
|
||||
<col
|
||||
className={cx('Table-expand-col')}
|
||||
style={{
|
||||
width: (expandable?.columnWidth || DefaultCellWidth) + 'px'
|
||||
}}
|
||||
></col>
|
||||
) : null}
|
||||
{isLeftExpandable ? expandableCol : null}
|
||||
{tdColumns.map((data, index) => {
|
||||
const width = colWidths ? colWidths[index + extraCount] : data.width;
|
||||
const width = colWidths
|
||||
? colWidths[index + extraCount - (isRightExpandable ? 1 : 0)]
|
||||
: data.width;
|
||||
|
||||
return (
|
||||
<col
|
||||
@ -831,6 +843,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
></col>
|
||||
);
|
||||
})}
|
||||
{isRightExpandable ? expandableCol : null}
|
||||
</colgroup>
|
||||
);
|
||||
}
|
||||
@ -932,6 +945,18 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
: this.state.dataSource;
|
||||
|
||||
const isExpandable = this.isExpandableTable();
|
||||
const isLeftExpandable = this.isLeftExpandable();
|
||||
const isRightExpandable = this.isRightExpandable();
|
||||
|
||||
const expandableCell =
|
||||
!draggable && isExpandable ? (
|
||||
<Cell
|
||||
wrapperComponent="th"
|
||||
rowSpan={thColumns.length}
|
||||
fixed={expandable && expandable.fixed ? 'left' : ''}
|
||||
className={cx('Table-row-expand-icon-cell')}
|
||||
></Cell>
|
||||
) : null;
|
||||
|
||||
let allRowKeys: Array<string> = [];
|
||||
let allRows: Array<any> = [];
|
||||
@ -1016,14 +1041,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
: null}
|
||||
</Cell>
|
||||
) : null}
|
||||
{!draggable && isExpandable && index === 0 ? (
|
||||
<Cell
|
||||
wrapperComponent="th"
|
||||
rowSpan={thColumns.length}
|
||||
fixed={expandable && expandable.fixed ? 'left' : ''}
|
||||
className={cx('Table-row-expand-icon-cell')}
|
||||
></Cell>
|
||||
) : null}
|
||||
{isLeftExpandable && index === 0 ? expandableCell : null}
|
||||
{data.map((item, i) => {
|
||||
let sort = null;
|
||||
if (item.sorter) {
|
||||
@ -1108,6 +1126,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
</Cell>
|
||||
);
|
||||
})}
|
||||
{isRightExpandable && index === 0 ? expandableCell : null}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
@ -1205,21 +1224,24 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
this.setState({hoverRow: null});
|
||||
}
|
||||
|
||||
onExpandRow(data: any) {
|
||||
onExpandRows(data: Array<any>) {
|
||||
const {expandedRowKeys} = this.state;
|
||||
const {expandable} = this.props;
|
||||
const key = data[this.getExpandableKeyField()];
|
||||
this.setState({expandedRowKeys: [...expandedRowKeys, key]});
|
||||
const keys = data.map((d: any) => d[this.getExpandableKeyField()]);
|
||||
this.setState({expandedRowKeys: [...expandedRowKeys, ...keys]});
|
||||
expandable?.onExpand && expandable?.onExpand(true, data);
|
||||
}
|
||||
|
||||
onCollapseRow(data: any) {
|
||||
onCollapseRows(data: Array<any>) {
|
||||
const {expandedRowKeys} = this.state;
|
||||
const {expandable} = this.props;
|
||||
const key = data[this.getExpandableKeyField()];
|
||||
// 还是得模糊匹配 否则'3'、3匹配不上
|
||||
this.setState({expandedRowKeys: expandedRowKeys.filter(k => k != key)});
|
||||
expandable?.onExpand && expandable?.onExpand(false, data);
|
||||
const keys = data.map((d: any) => d[this.getExpandableKeyField()]);
|
||||
this.setState({
|
||||
expandedRowKeys: expandedRowKeys.filter(
|
||||
(k: string | number) => !keys.find(v => v == k) // 模糊匹配 否则'3'、3匹配不上
|
||||
)
|
||||
});
|
||||
expandable?.onExpand && expandable?.onExpand(true, data);
|
||||
}
|
||||
|
||||
getChildrenColumnName() {
|
||||
@ -1251,8 +1273,8 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
|
||||
return (
|
||||
expandable &&
|
||||
expandable.rowExpandable &&
|
||||
expandable.rowExpandable(data, rowIndex)
|
||||
(!expandable.rowExpandable ||
|
||||
(expandable.rowExpandable && expandable.rowExpandable(data, rowIndex)))
|
||||
);
|
||||
}
|
||||
|
||||
@ -1295,20 +1317,27 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
return length > 0;
|
||||
}
|
||||
|
||||
getExpandedIcons(isExpanded: boolean, record: any) {
|
||||
isExpanded(record: any) {
|
||||
return !!find(
|
||||
this.state.expandedRowKeys,
|
||||
key => key == record[this.getExpandableKeyField()]
|
||||
); // == 匹配 否则'3'、3匹配不上
|
||||
}
|
||||
|
||||
getExpandedIcons(record: any) {
|
||||
const {classnames: cx} = this.props;
|
||||
|
||||
return isExpanded ? (
|
||||
return this.isExpanded(record) ? (
|
||||
<i
|
||||
className={cx('Table-expandBtn', 'is-active')}
|
||||
onClick={this.onCollapseRow.bind(this, record)}
|
||||
onClick={this.onCollapseRows.bind(this, [record])}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</i>
|
||||
) : (
|
||||
<i
|
||||
className={cx('Table-expandBtn')}
|
||||
onClick={this.onExpandRow.bind(this, record)}
|
||||
onClick={this.onExpandRows.bind(this, [record])}
|
||||
>
|
||||
<Icon icon="right-arrow-bold" className="icon" />
|
||||
</i>
|
||||
@ -1375,17 +1404,15 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
const isExpandable = this.isExpandableTable();
|
||||
const defaultKey = this.getRowSelectionKeyField();
|
||||
const colCount = this.getExtraColumnCount();
|
||||
const isLeftExpandable = this.isLeftExpandable();
|
||||
const isRightExpandable = this.isRightExpandable();
|
||||
|
||||
// 当前行是否可展开
|
||||
const isExpandableRow = this.isExpandableRow(data, rowIndex);
|
||||
// 当前行是否有children
|
||||
const hasChildrenRow = this.hasChildrenRow(data);
|
||||
|
||||
const isExpanded = !!find(
|
||||
this.state.expandedRowKeys,
|
||||
key => key == data[this.getExpandableKeyField()]
|
||||
); // == 匹配 否则'3'、3匹配不上
|
||||
|
||||
const isExpanded = this.isExpanded(data);
|
||||
// 设置缩进效果
|
||||
const indentDom =
|
||||
levels.length > 0 ? (
|
||||
@ -1439,9 +1466,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
})}
|
||||
>
|
||||
{i === 0 && levels.length > 0 ? indentDom : null}
|
||||
{i === 0 && hasChildrenRow
|
||||
? this.getExpandedIcons(isExpanded, data)
|
||||
: null}
|
||||
{i === 0 && hasChildrenRow ? this.getExpandedIcons(data) : null}
|
||||
{render ? children : data[item.name]}
|
||||
</div>
|
||||
</Cell>
|
||||
@ -1497,6 +1522,24 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
const hasChildrenChecked = this.hasCheckedChildrenRows(data);
|
||||
const isRadio = rowSelection && rowSelection.type === 'radio';
|
||||
|
||||
const expandableCell =
|
||||
!draggable && isExpandable ? (
|
||||
<Cell
|
||||
fixed={
|
||||
expandable && expandable.fixed
|
||||
? isRightExpandable
|
||||
? 'right'
|
||||
: 'left'
|
||||
: ''
|
||||
}
|
||||
className={cx('Table-cell-expand-icon-cell')}
|
||||
>
|
||||
{isExpandableRow || hasChildrenRow
|
||||
? this.getExpandedIcons(data)
|
||||
: null}
|
||||
</Cell>
|
||||
) : null;
|
||||
|
||||
return [
|
||||
<tr
|
||||
key={`${data[keyField || 'key'] || rowIndex}`} // 可能会拖拽排序,就不能用rowIndex作为key了,否则显示会有问题
|
||||
@ -1540,17 +1583,9 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
></CheckBox>
|
||||
</Cell>
|
||||
) : null}
|
||||
{!draggable && isExpandable ? (
|
||||
<Cell
|
||||
fixed={expandable && expandable.fixed ? 'left' : ''}
|
||||
className={cx('Table-cell-expand-icon-cell')}
|
||||
>
|
||||
{isExpandableRow || hasChildrenRow
|
||||
? this.getExpandedIcons(isExpanded, data)
|
||||
: null}
|
||||
</Cell>
|
||||
) : null}
|
||||
{isLeftExpandable ? expandableCell : null}
|
||||
{cells}
|
||||
{isRightExpandable ? expandableCell : null}
|
||||
</tr>,
|
||||
children
|
||||
];
|
||||
@ -1600,19 +1635,35 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
return !!expandable;
|
||||
}
|
||||
|
||||
// 展开列放到右侧 会影响之前的一些合并的规则
|
||||
isRightExpandable() {
|
||||
const {expandable} = this.props;
|
||||
return expandable && expandable.position === 'right';
|
||||
}
|
||||
|
||||
// 展开列放到左侧
|
||||
isLeftExpandable() {
|
||||
const {expandable} = this.props;
|
||||
return (
|
||||
expandable && (!expandable.position || expandable.position === 'left')
|
||||
);
|
||||
}
|
||||
|
||||
isNestedTable() {
|
||||
const {dataSource} = this.props;
|
||||
return !!find(dataSource, item => this.hasChildrenRow(item));
|
||||
}
|
||||
|
||||
// 计算自动增加的列数
|
||||
// 选择、拖拽、展开
|
||||
getExtraColumnCount() {
|
||||
const {draggable, rowSelection} = this.props;
|
||||
const {draggable, rowSelection, expandable} = this.props;
|
||||
|
||||
let count = 0;
|
||||
if (draggable) {
|
||||
count++;
|
||||
} else {
|
||||
if (this.isExpandableTable()) {
|
||||
if (this.isExpandableTable() && expandable?.position !== 'none') {
|
||||
count++;
|
||||
}
|
||||
if (rowSelection) {
|
||||
@ -1628,6 +1679,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
const cells: Array<React.ReactNode> = [];
|
||||
const trs: Array<React.ReactNode> = [];
|
||||
let colCount = this.getExtraColumnCount();
|
||||
const isRightExpandable = this.isRightExpandable() ? 1 : 0;
|
||||
|
||||
Array.isArray(summary)
|
||||
? summary.forEach((s, index) => {
|
||||
@ -1642,7 +1694,11 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
{s.map((d, i) => {
|
||||
// 将操作列自动添加到第一列,用户的colSpan只需要关心实际的列数
|
||||
const colSpan =
|
||||
i === 0 ? (d.colSpan || 1) + colCount : d.colSpan;
|
||||
i === 0
|
||||
? (d.colSpan || 1) + colCount - isRightExpandable
|
||||
: i === s.length - 1
|
||||
? (d.colSpan || 1) + isRightExpandable
|
||||
: d.colSpan;
|
||||
return (
|
||||
<Cell
|
||||
key={'summary-tr-cell-' + i}
|
||||
@ -1662,7 +1718,11 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
key={'summary-cell-' + index}
|
||||
fixed={s.fixed}
|
||||
colSpan={
|
||||
cells.length === 0 ? (s.colSpan || 1) + colCount : s.colSpan
|
||||
cells.length === 0
|
||||
? (s.colSpan || 1) + colCount - isRightExpandable
|
||||
: index === summary.length - 1
|
||||
? (s.colSpan || 1) + isRightExpandable
|
||||
: s.colSpan
|
||||
}
|
||||
>
|
||||
{typeof s.render === 'function'
|
||||
@ -1776,10 +1836,8 @@ export class Table extends React.PureComponent<TableProps, TableState> {
|
||||
// 设置了横向滚动轴 则table的table-layout为fixed
|
||||
const hasScrollX = scroll && scroll.x;
|
||||
const hoverRow = this.state.hoverRow;
|
||||
// 如果设置了列宽 那么table-layout为fixed才能生效
|
||||
const columnWidth = this.tdColumns.some(item => item.width);
|
||||
|
||||
const tableLayout = hasScrollX || columnWidth ? 'fixed' : 'auto';
|
||||
const tableLayout = hasScrollX ? 'fixed' : 'auto';
|
||||
const tableStyle = hasScrollX ? {width: scroll.x + 'px'} : {};
|
||||
|
||||
return (
|
||||
|
@ -44,6 +44,7 @@ register('de-DE', {
|
||||
'CRUD.invalidArray': '"data.items" muss ein Array sein',
|
||||
'CRUD.invalidData': '"data" ist leer',
|
||||
'CRUD.loadMore': 'Weitere laden',
|
||||
'CRUD.loadMoreDisableTip': 'Keine Daten oder letzte Seite',
|
||||
'CRUD.perPage': 'Pro Seite',
|
||||
'CRUD.stat': '{{page}} von {{lastPage}} insgesamt: {{total}}.',
|
||||
'CRUD.paginationGoText': 'Wechseln zu',
|
||||
|
@ -39,6 +39,7 @@ register('en-US', {
|
||||
'CRUD.invalidArray': 'data.items must be an array',
|
||||
'CRUD.invalidData': 'data is empty',
|
||||
'CRUD.loadMore': 'Load more',
|
||||
'CRUD.loadMoreDisableTip': 'No data or last page',
|
||||
'CRUD.perPage': 'Per page',
|
||||
'CRUD.stat': '{{page}} of {{lastPage}} total: {{total}}.',
|
||||
'CRUD.paginationGoText': 'Go to',
|
||||
|
@ -42,6 +42,7 @@ register('zh-CN', {
|
||||
'CRUD.invalidArray': 'data.items 必须是数组',
|
||||
'CRUD.invalidData': '返回数据格式不正确,data 没有数据',
|
||||
'CRUD.loadMore': '加载更多',
|
||||
'CRUD.loadMoreDisableTip': '无数据或最后一页',
|
||||
'CRUD.perPage': '每页显示',
|
||||
'CRUD.stat': '{{page}}/{{lastPage}} 总共:{{total}} 项',
|
||||
'CRUD.paginationGoText': '前往',
|
||||
|
@ -29,6 +29,7 @@ import '../../src';
|
||||
import {clearStoresCache, render as amisRender} from '../../src';
|
||||
import {makeEnv as makeEnvRaw, wait} from '../helper';
|
||||
import rows from '../mockData/rows';
|
||||
import type {RenderOptions} from '../../src';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
@ -37,7 +38,8 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
/** 避免updateLocation里的console.error */
|
||||
const makeEnv = args => makeEnvRaw({updateLocation: () => {}, ...args});
|
||||
const makeEnv = (env?: Partial<RenderOptions>) =>
|
||||
makeEnvRaw({updateLocation: () => {}, ...env});
|
||||
|
||||
async function fetcher(config: any) {
|
||||
return {
|
||||
|
@ -1,5 +1,21 @@
|
||||
import React = require('react');
|
||||
import {render, cleanup, fireEvent, waitFor} from '@testing-library/react';
|
||||
/**
|
||||
* 功能名称:autoFill 自动填充
|
||||
* 单测内容:
|
||||
* 01. 字段填充
|
||||
* 02. 字段填充 & 提交表单
|
||||
* 03. 字段填充的表单项name为对象路径格式
|
||||
* 04. 字段填充(多选模式)
|
||||
* 05. 字段填充后正常触发表单项校验 & validateOnChange正常触发
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
render,
|
||||
cleanup,
|
||||
screen,
|
||||
fireEvent,
|
||||
waitFor
|
||||
} from '@testing-library/react';
|
||||
import '../../../src';
|
||||
import {render as amisRender} from '../../../src';
|
||||
import {makeEnv, wait} from '../../helper';
|
||||
@ -10,6 +26,11 @@ afterEach(() => {
|
||||
clearStoresCache();
|
||||
});
|
||||
|
||||
const optisons = [
|
||||
{label: 'OptionA', value: 'a'},
|
||||
{label: 'OptionB', value: 'b'}
|
||||
];
|
||||
|
||||
test('Form:options:autoFill', async () => {
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
@ -151,6 +172,74 @@ test('Form:options:autoFill:data', async () => {
|
||||
expect(onSubmit.mock.calls[1][0]).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Form:options:autoFill:keyWithPath', async () => {
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'form',
|
||||
body: [
|
||||
{
|
||||
type: 'radios',
|
||||
label: 'trigger',
|
||||
name: 'trigger',
|
||||
autoFill: {
|
||||
'receiver.target1': '${value}',
|
||||
'receiver.target2': '${value}'
|
||||
},
|
||||
options: [
|
||||
{label: 'OptionA', value: 'a'},
|
||||
{label: 'OptionB', value: 'b'}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'receiver.target1',
|
||||
label: 'receiver.target1'
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'receiver.target2',
|
||||
label: 'receiver.target2'
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'standalone',
|
||||
label: 'standalone',
|
||||
value: 'abc'
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv()
|
||||
)
|
||||
);
|
||||
|
||||
const triggerA = await screen.findByText(/OptionA/);
|
||||
const triggerB = await screen.findByText(/OptionB/);
|
||||
const target1 = container.querySelector(
|
||||
'input[name=receiver\\.target1]'
|
||||
) as HTMLInputElement;
|
||||
const target2 = container.querySelector(
|
||||
'input[name=receiver\\.target2]'
|
||||
) as HTMLInputElement;
|
||||
const standalone = container.querySelector(
|
||||
'input[name=standalone]'
|
||||
) as HTMLInputElement;
|
||||
|
||||
fireEvent.click(triggerA);
|
||||
await wait(1000);
|
||||
expect(target1.getAttribute('value')).toEqual('a');
|
||||
expect(target2.getAttribute('value')).toEqual('a');
|
||||
expect(standalone.getAttribute('value')).toEqual('abc');
|
||||
|
||||
fireEvent.click(triggerB);
|
||||
await wait(1000);
|
||||
|
||||
expect(target1.getAttribute('value')).toEqual('b');
|
||||
expect(target2.getAttribute('value')).toEqual('b');
|
||||
expect(standalone.getAttribute('value')).toEqual('abc');
|
||||
});
|
||||
|
||||
test('Form:options:autoFill:multiple:data', async () => {
|
||||
const onSubmit = jest.fn();
|
||||
const {container, getByText, findByText} = render(
|
||||
@ -206,3 +295,107 @@ test('Form:options:autoFill:multiple:data', async () => {
|
||||
expect(onSubmit).toBeCalledTimes(2);
|
||||
expect(onSubmit.mock.calls[1][0]).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Form:options:autoFill:validation', async () => {
|
||||
const onSubmitFn = jest.fn();
|
||||
const submitText = 'Submit';
|
||||
const validationMsg1 = '选项1校验失败,数据必须为Option B';
|
||||
const validationMsg2 = '选项2校验失败,数据必须为Option B';
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'form',
|
||||
submitText,
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
label: '选项',
|
||||
name: 'select',
|
||||
placeholder: 'SelectCompt',
|
||||
autoFill: {
|
||||
instantValidate: '${label}',
|
||||
submitValidate: '${label}'
|
||||
},
|
||||
clearable: true,
|
||||
options: [
|
||||
{label: 'OptionA', value: 'a'},
|
||||
{label: 'OptionB', value: 'b'}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'instantValidate',
|
||||
label: '选项1',
|
||||
placeholder: '选项1',
|
||||
description: '填充后立即校验',
|
||||
required: true,
|
||||
validateOnChange: true,
|
||||
validations: {
|
||||
equals: 'OptionB'
|
||||
},
|
||||
validationErrors: {
|
||||
equals: validationMsg1
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'submitValidate',
|
||||
label: '选项2',
|
||||
placeholder: '选项2',
|
||||
description: '填充后提交表单时才校验',
|
||||
required: true,
|
||||
validations: {
|
||||
equals: 'OptionB'
|
||||
},
|
||||
validationErrors: {
|
||||
equals: validationMsg2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{onSubmit: onSubmitFn},
|
||||
makeEnv({})
|
||||
)
|
||||
);
|
||||
|
||||
const select = container.querySelector(
|
||||
'span[class*=Select-arrow]'
|
||||
) as HTMLDivElement;
|
||||
const option1 = container.querySelector(
|
||||
'input[name=instantValidate]'
|
||||
) as HTMLInputElement;
|
||||
const option2 = container.querySelector(
|
||||
'input[name=submitValidate]'
|
||||
) as HTMLInputElement;
|
||||
const submitBtn = screen.getByRole('button', {name: submitText});
|
||||
|
||||
// 自动填充触发后生成校验信息
|
||||
fireEvent.click(select);
|
||||
await wait(300);
|
||||
fireEvent.click(screen.getByText(/OptionA/));
|
||||
await wait(1000);
|
||||
expect(option1.getAttribute('value')).toEqual('OptionA');
|
||||
expect(option2.getAttribute('value')).toEqual('OptionA');
|
||||
expect(screen.queryByText(validationMsg1)).toBeInTheDocument();
|
||||
|
||||
// 提交后校验选项2
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(500);
|
||||
expect(screen.queryByText(validationMsg2)).toBeInTheDocument();
|
||||
|
||||
// 自动填充再次触发后validateOnChange的选项消除校验信息
|
||||
fireEvent.click(select);
|
||||
await wait(300);
|
||||
fireEvent.click(screen.getByText(/OptionB/));
|
||||
await wait(1000);
|
||||
expect(option1.getAttribute('value')).toEqual('OptionB');
|
||||
expect(option1.getAttribute('value')).toEqual('OptionB');
|
||||
expect(screen.queryByText(validationMsg1)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(validationMsg2)).toBeInTheDocument();
|
||||
|
||||
// 提交后校验信息全部消除
|
||||
fireEvent.click(submitBtn);
|
||||
await wait(500);
|
||||
expect(screen.queryByText(validationMsg1)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(validationMsg2)).not.toBeInTheDocument();
|
||||
});
|
||||
|
86
packages/amis/__tests__/renderers/Form/inputImage.test.tsx
Normal file
86
packages/amis/__tests__/renderers/Form/inputImage.test.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import React = require('react');
|
||||
import PageRenderer from '../../../../amis-core/src/renderers/Form';
|
||||
import * as renderer from 'react-test-renderer';
|
||||
import {
|
||||
render,
|
||||
fireEvent,
|
||||
waitFor,
|
||||
getByText,
|
||||
prettyDOM
|
||||
} from '@testing-library/react';
|
||||
import '../../../src';
|
||||
import {render as amisRender} from '../../../src';
|
||||
import {makeEnv, wait} from '../../helper';
|
||||
|
||||
test('Renderer:input-image autoFill', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
value: 'img.png',
|
||||
filename: 'filename.png',
|
||||
myUrl: 'http://amis.com/image.png'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
global.URL.createObjectURL = jest.fn();
|
||||
|
||||
const {
|
||||
debug,
|
||||
container,
|
||||
findByText,
|
||||
getByLabelText,
|
||||
findByPlaceholderText,
|
||||
findByDisplayValue
|
||||
} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'form',
|
||||
api: '/api/xxx',
|
||||
body: [
|
||||
{
|
||||
type: 'input-image',
|
||||
name: 'img',
|
||||
label: 'img',
|
||||
receiver: '/api/upload/file',
|
||||
autoFill: {
|
||||
// 不知道为啥这里不能用 ${url},可能是有什么地方和真实浏览器不一致
|
||||
myUrl: '${myUrl}'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'myUrl'
|
||||
}
|
||||
],
|
||||
title: 'The form',
|
||||
actions: []
|
||||
},
|
||||
{},
|
||||
makeEnv({fetcher})
|
||||
)
|
||||
);
|
||||
|
||||
const fileInput = container.querySelector(
|
||||
'input[type=file]'
|
||||
)! as HTMLInputElement;
|
||||
|
||||
const file = new File(['file'], 'ping.png', {
|
||||
type: 'image/png'
|
||||
});
|
||||
|
||||
fireEvent.change(fileInput, {
|
||||
target: {files: [file]}
|
||||
});
|
||||
|
||||
await wait(500);
|
||||
|
||||
const textInput = container.querySelector(
|
||||
'input[name=myUrl]'
|
||||
)! as HTMLInputElement;
|
||||
|
||||
expect(textInput.value).toBe('http://amis.com/image.png');
|
||||
});
|
@ -324,319 +324,351 @@ exports[`Renderer:breadcrumb separator 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
当前页面
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 2`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
当前页面
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 3`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
当前页面
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb tooltip labelMaxLength 4`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Breadcrumb"
|
||||
>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
href="https://baidu.gitee.com/"
|
||||
>
|
||||
<i
|
||||
className="cxd-Icon fa fa-home cxd-Breadcrumb-icon"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item"
|
||||
>
|
||||
<a
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
上级页面上级页面上级页面上级页面...
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-separator text-black"
|
||||
>
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-Breadcrumb-item cxd-Breadcrumb-item-last"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Breadcrumb-item-default"
|
||||
>
|
||||
当前页面
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
当前页面
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:breadcrumb var 1`] = `
|
||||
|
@ -1,55 +1,72 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Renderer:Page 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-headerRow"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-header"
|
||||
className="cxd-Page-headerRow"
|
||||
>
|
||||
<h2
|
||||
className="cxd-Page-title"
|
||||
<div
|
||||
className="cxd-Page-header"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
<h2
|
||||
className="cxd-Page-title"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "This is Title",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
className="cxd-Remark cxd-Remark--warning"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-Remark-icon icon"
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<icon-mock
|
||||
className=" icon-question-mark"
|
||||
icon="question-mark"
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "This is Title",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</h2>
|
||||
<small
|
||||
className="cxd-Page-subTitle"
|
||||
<div
|
||||
className="cxd-Remark cxd-Remark--warning"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<span
|
||||
className="cxd-Remark-icon icon"
|
||||
>
|
||||
<icon-mock
|
||||
className=" icon-question-mark"
|
||||
icon="question-mark"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</h2>
|
||||
<small
|
||||
className="cxd-Page-subTitle"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "This is SubTitle",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
<div
|
||||
className="cxd-Page-toolbar"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
@ -57,47 +74,38 @@ exports[`Renderer:Page 1`] = `
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "This is SubTitle",
|
||||
"__html": "This is toolbar",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="cxd-Page-toolbar"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "This is toolbar",
|
||||
"__html": "This is body",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "This is body",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page classNames 1`] = `
|
||||
@ -979,87 +987,81 @@ exports[`Renderer:Page initApi error show Message 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page initApi reFetch when condition changes 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Spinner-overlay in"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Spinner cxd-Spinner--overlay in"
|
||||
data-testid="spinner"
|
||||
>
|
||||
<div
|
||||
className="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 1",
|
||||
}
|
||||
}
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page initApi reFetch when condition changes 2`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Spinner-overlay in"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Spinner cxd-Spinner--overlay in"
|
||||
data-testid="spinner"
|
||||
>
|
||||
<div
|
||||
className="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 2",
|
||||
}
|
||||
}
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 2",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page initApi reload by Dialog action 1`] = `
|
||||
@ -1972,136 +1974,157 @@ exports[`Renderer:Page initApi silentPolling 2`] = `
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page initData 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 1",
|
||||
}
|
||||
}
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page initFetchOn trigger initApi fetch when condition becomes ture 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<div
|
||||
className="cxd-Spinner-overlay in"
|
||||
/>
|
||||
<div
|
||||
className="cxd-Spinner cxd-Spinner--overlay in"
|
||||
data-testid="spinner"
|
||||
>
|
||||
<div
|
||||
className="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 6",
|
||||
}
|
||||
}
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 6",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page location query 1`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 5",
|
||||
}
|
||||
}
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 5",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Renderer:Page location query 2`] = `
|
||||
<div
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
[
|
||||
<div
|
||||
className="cxd-Page-content"
|
||||
className="cxd-Page"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-main"
|
||||
className="cxd-Page-content"
|
||||
>
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
className="cxd-Page-main"
|
||||
>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
<div
|
||||
className="cxd-Page-body"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 6",
|
||||
}
|
||||
}
|
||||
className="cxd-Spinner-mark"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="cxd-TplField"
|
||||
>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
{
|
||||
"__html": "The variable value is 6",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<span
|
||||
className="cxd-Spinner-mark"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amis",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"description": "一种MIS页面生成工具",
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
@ -40,8 +40,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"amis-core": "^2.2.0",
|
||||
"amis-ui": "^2.2.0",
|
||||
"amis-core": "^2.3.0",
|
||||
"amis-ui": "^2.3.0",
|
||||
"attr-accept": "2.2.2",
|
||||
"blueimp-canvastoblob": "2.1.0",
|
||||
"classnames": "2.3.1",
|
||||
@ -83,7 +83,7 @@
|
||||
"@rollup/plugin-typescript": "^8.3.4",
|
||||
"@svgr/rollup": "^6.2.1",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@types/async": "^2.0.45",
|
||||
"@types/codemirror": "^5.60.3",
|
||||
"@types/echarts": "^4.9.2",
|
||||
@ -215,7 +215,11 @@
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"/.rollup.cache/"
|
||||
]
|
||||
],
|
||||
"snapshotFormat": {
|
||||
"escapeString": false,
|
||||
"printBasicPrototype": false
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"amis-core": "*",
|
||||
|
@ -215,9 +215,15 @@ export default class App extends React.Component<AppProps, object> {
|
||||
this.unWatchRouteChange?.();
|
||||
}
|
||||
|
||||
async reload(subpath?: any, query?: any, ctx?: any, silent?: boolean) {
|
||||
async reload(
|
||||
subpath?: any,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
}
|
||||
|
||||
const {
|
||||
@ -246,10 +252,10 @@ export default class App extends React.Component<AppProps, object> {
|
||||
}
|
||||
}
|
||||
|
||||
receive(values: object) {
|
||||
receive(values: object, subPath?: string, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
|
||||
store.updateData(values);
|
||||
store.updateData(values, undefined, replace);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@ -468,7 +474,7 @@ export class AppRenderer extends App {
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
|
||||
setData(values: object) {
|
||||
return this.props.store.updateData(values);
|
||||
setData(values: object, replace?: boolean) {
|
||||
return this.props.store.updateData(values, undefined, replace);
|
||||
}
|
||||
}
|
||||
|
@ -1501,9 +1501,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
}
|
||||
}
|
||||
|
||||
handleQuery(values: object, forceReload: boolean = false) {
|
||||
handleQuery(values: object, forceReload: boolean = false, replace?: boolean) {
|
||||
const {store, syncLocation, env, pageField, perPageField} = this.props;
|
||||
|
||||
store.updateQuery(
|
||||
{
|
||||
...values,
|
||||
@ -1513,21 +1512,22 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
? env.updateLocation
|
||||
: undefined,
|
||||
pageField,
|
||||
perPageField
|
||||
perPageField,
|
||||
replace
|
||||
);
|
||||
this.search(undefined, undefined, undefined, forceReload);
|
||||
}
|
||||
|
||||
reload(subpath?: string, query?: any) {
|
||||
reload(subpath?: string, query?: any, replace?: boolean) {
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
} else {
|
||||
this.search(undefined, undefined, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
receive(values: object) {
|
||||
this.handleQuery(values, true);
|
||||
receive(values: object, subPath?: string, replace?: boolean) {
|
||||
this.handleQuery(values, true, replace);
|
||||
}
|
||||
|
||||
reloadTarget(target: string, data: any) {
|
||||
@ -1807,9 +1807,11 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
const {store, classPrefix: ns, classnames: cx, translate: __} = this.props;
|
||||
const {page, lastPage} = store;
|
||||
|
||||
return page < lastPage ? (
|
||||
return (
|
||||
<div className={cx('Crud-loadMore')}>
|
||||
<Button
|
||||
disabled={page >= lastPage}
|
||||
disabledTip={__('CRUD.loadMoreDisableTip')}
|
||||
classPrefix={ns}
|
||||
onClick={() =>
|
||||
this.search({page: page + 1, loadDataMode: 'load-more'})
|
||||
@ -1819,8 +1821,6 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
{__('CRUD.loadMore')}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
@ -2244,7 +2244,13 @@ export class CRUDRenderer extends CRUD {
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
reload(subpath?: string, query?: any, ctx?: any) {
|
||||
reload(
|
||||
subpath?: string,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
const scoped = this.context as IScopedContext;
|
||||
if (subpath) {
|
||||
return scoped.reload(
|
||||
@ -2253,16 +2259,16 @@ export class CRUDRenderer extends CRUD {
|
||||
);
|
||||
}
|
||||
|
||||
return super.reload(subpath, query);
|
||||
return super.reload(subpath, query, replace);
|
||||
}
|
||||
|
||||
receive(values: any, subPath?: string) {
|
||||
receive(values: any, subPath?: string, replace?: boolean) {
|
||||
const scoped = this.context as IScopedContext;
|
||||
if (subPath) {
|
||||
return scoped.send(subPath, values);
|
||||
}
|
||||
|
||||
return super.receive(values);
|
||||
return super.receive(values, undefined, replace);
|
||||
}
|
||||
|
||||
reloadTarget(target: string, data: any) {
|
||||
|
@ -324,11 +324,17 @@ export class Chart extends React.Component<ChartProps> {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
reload(subpath?: string, query?: any) {
|
||||
reload(
|
||||
subpath?: string,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
const {api, env, store, interval, translate: __} = this.props;
|
||||
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
} else if (!env || !env.fetcher || !isEffectiveApi(api, store.data)) {
|
||||
return;
|
||||
}
|
||||
@ -389,10 +395,10 @@ export class Chart extends React.Component<ChartProps> {
|
||||
});
|
||||
}
|
||||
|
||||
receive(data: object) {
|
||||
receive(data: object, subPath?: string, replace?: boolean) {
|
||||
const store = this.props.store;
|
||||
|
||||
store.updateData(data);
|
||||
store.updateData(data, undefined, replace);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@ -508,9 +514,9 @@ export class ChartRenderer extends Chart {
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
setData(values: object) {
|
||||
setData(values: object, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
store.updateData(values);
|
||||
store.updateData(values, undefined, replace);
|
||||
// 重新渲染
|
||||
this.renderChart(this.props.config, values);
|
||||
}
|
||||
|
@ -100,6 +100,18 @@ export interface CollapseProps
|
||||
}
|
||||
|
||||
export default class Collapse extends React.Component<CollapseProps, {}> {
|
||||
static propsList: Array<string> = [
|
||||
'collapsable',
|
||||
'collapsed',
|
||||
'collapseTitle',
|
||||
'showArrow',
|
||||
'headerPosition',
|
||||
'bodyClassName',
|
||||
'headingClassName',
|
||||
'collapseHeader',
|
||||
'size'
|
||||
];
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
|
@ -66,7 +66,7 @@ export default class Container<T> extends React.Component<
|
||||
{children
|
||||
? typeof children === 'function'
|
||||
? ((children as any)(this.props) as JSX.Element)
|
||||
: (children as unknown)
|
||||
: (children as any)
|
||||
: body
|
||||
? (render('body', body as any, {disabled}) as JSX.Element)
|
||||
: null}
|
||||
|
@ -120,7 +120,7 @@ export interface ComboControlSchema extends FormBaseControlSchema {
|
||||
/**
|
||||
* Add at top
|
||||
*/
|
||||
addattop?: boolean;
|
||||
addattop?: boolean;
|
||||
|
||||
/**
|
||||
* 数组输入框的子项
|
||||
@ -464,8 +464,15 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
}
|
||||
|
||||
addItemWith(condition: ComboCondition) {
|
||||
const {flat, joinValues, addattop, delimiter, scaffold, disabled, submitOnChange} =
|
||||
this.props;
|
||||
const {
|
||||
flat,
|
||||
joinValues,
|
||||
addattop,
|
||||
delimiter,
|
||||
scaffold,
|
||||
disabled,
|
||||
submitOnChange
|
||||
} = this.props;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
@ -486,7 +493,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
value = value.join(delimiter || ',');
|
||||
}
|
||||
|
||||
if (addattop === true){
|
||||
if (addattop === true) {
|
||||
value.unshift(value.pop());
|
||||
}
|
||||
|
||||
@ -537,7 +544,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
value = value.join(delimiter || ',');
|
||||
}
|
||||
|
||||
if (addattop === true){
|
||||
if (addattop === true) {
|
||||
value.unshift(value.pop());
|
||||
}
|
||||
|
||||
@ -1623,7 +1630,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
})
|
||||
export class ComboControlRenderer extends ComboControl {
|
||||
// 支持更新指定索引的值
|
||||
setData(value: any, index?: number) {
|
||||
setData(value: any, replace?: boolean, index?: number) {
|
||||
const {multiple, onChange, submitOnChange} = this.props;
|
||||
if (multiple) {
|
||||
if (index !== undefined && ~index) {
|
||||
|
@ -89,6 +89,14 @@ export default class FieldSetControl extends React.Component<
|
||||
collapsable: false
|
||||
};
|
||||
|
||||
static propsList: Array<string> = [
|
||||
'collapsable',
|
||||
'collapsed',
|
||||
'collapseTitle',
|
||||
'titlePosition',
|
||||
'collapseTitle'
|
||||
];
|
||||
|
||||
renderBody(): JSX.Element {
|
||||
const {
|
||||
body,
|
||||
@ -116,7 +124,6 @@ export default class FieldSetControl extends React.Component<
|
||||
formHorizontal: subFormHorizontal || formHorizontal
|
||||
};
|
||||
mode && (props.mode = mode);
|
||||
typeof collapsable !== 'undefined' && (props.collapsable = collapsable);
|
||||
horizontal && (props.horizontal = horizontal);
|
||||
|
||||
return (
|
||||
|
@ -836,7 +836,7 @@ export default class ImageControl extends React.Component<
|
||||
// 排除自身的字段,否则会无限更新state
|
||||
const excludeSelfAutoFill = omit(autoFill, name || '');
|
||||
|
||||
if (!isEmpty(excludeSelfAutoFill) && onBulkChange && this.initAutoFill) {
|
||||
if (!isEmpty(excludeSelfAutoFill) && onBulkChange) {
|
||||
const files = this.state.files.filter(
|
||||
file => ~['uploaded', 'init', 'ready'].indexOf(file.state as string)
|
||||
);
|
||||
@ -1341,7 +1341,6 @@ export default class ImageControl extends React.Component<
|
||||
frameImageStyle.width = frameImageWidth;
|
||||
}
|
||||
const filterFrameImage = filter(frameImage, this.props.data, '| raw');
|
||||
|
||||
const hasPending = files.some(file => file.state == 'pending');
|
||||
return (
|
||||
<div className={cx(`ImageControl`, className)}>
|
||||
|
@ -63,18 +63,26 @@ export interface NumberControlSchema extends FormBaseControlSchema {
|
||||
*/
|
||||
unitOptions?: string | Array<Option> | string[] | PlainObject;
|
||||
|
||||
/**
|
||||
* 是否是大数,如果是的话输入输出都将是字符串
|
||||
*/
|
||||
big?: boolean;
|
||||
|
||||
/**
|
||||
* 是否千分分隔
|
||||
*/
|
||||
kilobitSeparator?: boolean;
|
||||
|
||||
/**
|
||||
* 只读
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
|
||||
/**
|
||||
* 是否启用键盘行为
|
||||
*/
|
||||
keyboard?: boolean;
|
||||
|
||||
/**
|
||||
* 输入框为基础输入框还是加强输入框
|
||||
*/
|
||||
@ -115,6 +123,10 @@ export interface NumberProps extends FormControlProps {
|
||||
* 输入框为基础输入框还是加强输入框
|
||||
*/
|
||||
displayMode?: 'base' | 'enhance';
|
||||
/**
|
||||
* 是否是大数,如果是的话输入输出都将是字符串
|
||||
*/
|
||||
big?: boolean;
|
||||
}
|
||||
|
||||
interface NumberState {
|
||||
@ -213,7 +225,11 @@ export default class NumberControl extends React.Component<
|
||||
getValue(inputValue: any) {
|
||||
const {resetValue, unitOptions} = this.props;
|
||||
|
||||
if (inputValue && typeof inputValue !== 'number') {
|
||||
if (
|
||||
inputValue &&
|
||||
typeof inputValue !== 'number' &&
|
||||
typeof inputValue !== 'string'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -253,6 +269,9 @@ export default class NumberControl extends React.Component<
|
||||
}
|
||||
|
||||
filterNum(value: number | string | undefined) {
|
||||
if (typeof value === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof value !== 'number') {
|
||||
value = filter(value, this.props.data);
|
||||
value = /^[-]?\d+/.test(value) ? +value : undefined;
|
||||
@ -315,7 +334,8 @@ export default class NumberControl extends React.Component<
|
||||
unitOptions,
|
||||
readOnly,
|
||||
keyboard,
|
||||
displayMode
|
||||
displayMode,
|
||||
big
|
||||
} = this.props;
|
||||
let precisionProps: any = {};
|
||||
const finalPrecision = this.filterNum(precision);
|
||||
@ -376,6 +396,7 @@ export default class NumberControl extends React.Component<
|
||||
onBlur={() => this.dispatchEvent('blur')}
|
||||
keyboard={keyboard}
|
||||
displayMode={displayMode}
|
||||
big={big}
|
||||
/>
|
||||
{unitOptions ? (
|
||||
<Select
|
||||
|
@ -239,7 +239,7 @@ export interface DefaultProps {
|
||||
|
||||
export interface RangeItemProps extends RangeProps {
|
||||
value: FormatValue;
|
||||
updateValue: (value: Value) => void;
|
||||
onChange: (value: Value) => void;
|
||||
onAfterChange: () => void;
|
||||
}
|
||||
|
||||
@ -298,7 +298,7 @@ export class Input extends React.Component<RangeItemProps, any> {
|
||||
const {multiple, value: originValue, type, min} = this.props;
|
||||
const _value = this.getValue(value, type);
|
||||
|
||||
this.props.updateValue(
|
||||
this.props.onChange(
|
||||
multiple
|
||||
? {...(originValue as MultipleValue), [type]: _value}
|
||||
: value ?? min
|
||||
@ -314,7 +314,7 @@ export class Input extends React.Component<RangeItemProps, any> {
|
||||
const {multiple, value: originValue, type} = this.props;
|
||||
const _value = this.getValue(value, type);
|
||||
|
||||
this.props.updateValue(
|
||||
this.props.onChange(
|
||||
multiple ? {...(originValue as MultipleValue), [type]: _value} : value
|
||||
);
|
||||
}
|
||||
@ -547,11 +547,11 @@ export default class RangeControl extends React.PureComponent<
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有触发value变换 -> updateValue
|
||||
* 所有触发value变换 -> onChange
|
||||
* @param value
|
||||
*/
|
||||
@autobind
|
||||
async updateValue(value: FormatValue) {
|
||||
async onChange(value: FormatValue) {
|
||||
this.setState({value: this.getValue(value)});
|
||||
const {onChange, data, dispatchEvent} = this.props;
|
||||
const result = this.getFormatValue(value);
|
||||
@ -604,7 +604,7 @@ export default class RangeControl extends React.PureComponent<
|
||||
const props: RangeItemProps = {
|
||||
...this.props,
|
||||
value,
|
||||
updateValue: this.updateValue,
|
||||
onChange: this.onChange,
|
||||
onAfterChange: this.onAfterChange
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
import React from 'react';
|
||||
import {FormItem, FormControlProps, FormBaseControl} from 'amis-core';
|
||||
import {
|
||||
FormItem,
|
||||
FormControlProps,
|
||||
FormBaseControl,
|
||||
buildApi,
|
||||
qsstringify
|
||||
} from 'amis-core';
|
||||
import cx from 'classnames';
|
||||
import {LazyComponent} from 'amis-core';
|
||||
import {tokenize} from 'amis-core';
|
||||
@ -108,8 +114,23 @@ export default class RichTextControl extends React.Component<
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
|
||||
const imageReceiver = normalizeApi(
|
||||
props.receiver,
|
||||
props.receiver.method || 'post'
|
||||
);
|
||||
imageReceiver.data = imageReceiver.data || {};
|
||||
const imageApi = buildApi(imageReceiver, props.data, {
|
||||
method: props.receiver.method || 'post'
|
||||
});
|
||||
if (finnalVendor === 'froala') {
|
||||
const videoReceiver = normalizeApi(
|
||||
props.videoReceiver,
|
||||
props.videoReceiver.method || 'post'
|
||||
);
|
||||
videoReceiver.data = videoReceiver.data || {};
|
||||
const videoApi = buildApi(videoReceiver, props.data, {
|
||||
method: props.videoReceiver.method || 'post'
|
||||
});
|
||||
this.config = {
|
||||
imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif'],
|
||||
imageDefaultAlign: 'left',
|
||||
@ -135,13 +156,15 @@ export default class RichTextControl extends React.Component<
|
||||
...props.options,
|
||||
editorClass: props.editorClass,
|
||||
placeholderText: props.translate(props.placeholder),
|
||||
imageUploadURL: tokenize(props.receiver, props.data),
|
||||
imageUploadURL: imageApi.url,
|
||||
imageUploadParams: {
|
||||
from: 'rich-text'
|
||||
from: 'rich-text',
|
||||
...imageApi.data
|
||||
},
|
||||
videoUploadURL: tokenize(props.videoReceiver, props.data),
|
||||
videoUploadURL: videoApi.url,
|
||||
videoUploadParams: {
|
||||
from: 'rich-text'
|
||||
from: 'rich-text',
|
||||
...videoApi.data
|
||||
},
|
||||
events: {
|
||||
...(props.options && props.options.events),
|
||||
@ -162,8 +185,19 @@ export default class RichTextControl extends React.Component<
|
||||
images_upload_handler: (blobInfo: any, progress: any) =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
const formData = new FormData();
|
||||
|
||||
if (imageApi.data) {
|
||||
qsstringify(imageApi.data)
|
||||
.split('&')
|
||||
.filter(item => item !== '')
|
||||
.forEach(item => {
|
||||
let parts = item.split('=');
|
||||
formData.append(parts[0], decodeURIComponent(parts[1]));
|
||||
});
|
||||
}
|
||||
|
||||
formData.append(
|
||||
props.fileField,
|
||||
props.fileField || 'file',
|
||||
blobInfo.blob(),
|
||||
blobInfo.filename()
|
||||
);
|
||||
@ -176,7 +210,7 @@ export default class RichTextControl extends React.Component<
|
||||
data: payload
|
||||
};
|
||||
},
|
||||
...normalizeApi(tokenize(props.receiver, props.data), 'post')
|
||||
...imageApi
|
||||
};
|
||||
|
||||
const response = await fetcher(receiver, formData, {
|
||||
|
@ -620,9 +620,15 @@ export default class Page extends React.Component<PageProps> {
|
||||
});
|
||||
}
|
||||
|
||||
reload(subpath?: any, query?: any, ctx?: any, silent?: boolean) {
|
||||
reload(
|
||||
subpath?: any,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
}
|
||||
|
||||
const {store, initApi} = this.props;
|
||||
@ -636,10 +642,10 @@ export default class Page extends React.Component<PageProps> {
|
||||
.then(this.initInterval);
|
||||
}
|
||||
|
||||
receive(values: object) {
|
||||
receive(values: object, subPath?: string, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
|
||||
store.updateData(values);
|
||||
store.updateData(values, undefined, replace);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@ -1024,7 +1030,7 @@ export class PageRenderer extends Page {
|
||||
}, 300);
|
||||
}
|
||||
|
||||
setData(values: object) {
|
||||
return this.props.store.updateData(values);
|
||||
setData(values: object, replace?: boolean) {
|
||||
return this.props.store.updateData(values, undefined, replace);
|
||||
}
|
||||
}
|
||||
|
@ -535,9 +535,15 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
return value;
|
||||
}
|
||||
|
||||
reload(subpath?: string, query?: any, ctx?: RendererData, silent?: boolean) {
|
||||
reload(
|
||||
subpath?: string,
|
||||
query?: any,
|
||||
ctx?: RendererData,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
}
|
||||
|
||||
const {
|
||||
@ -587,10 +593,10 @@ export default class Service extends React.Component<ServiceProps> {
|
||||
this.reload(target, query, undefined, true);
|
||||
}
|
||||
|
||||
receive(values: object) {
|
||||
receive(values: object, subPath?: string, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
|
||||
store.updateData(values);
|
||||
store.updateData(values, undefined, replace);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@ -772,7 +778,13 @@ export class ServiceRenderer extends Service {
|
||||
scoped.registerComponent(this as ScopedComponentType);
|
||||
}
|
||||
|
||||
reload(subpath?: string, query?: any, ctx?: any, silent?: boolean) {
|
||||
reload(
|
||||
subpath?: string,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
const scoped = this.context as IScopedContext;
|
||||
if (subpath) {
|
||||
return scoped.reload(
|
||||
@ -781,16 +793,16 @@ export class ServiceRenderer extends Service {
|
||||
);
|
||||
}
|
||||
|
||||
return super.reload(subpath, query, ctx, silent);
|
||||
return super.reload(subpath, query, ctx, silent, replace);
|
||||
}
|
||||
|
||||
receive(values: any, subPath?: string) {
|
||||
receive(values: any, subPath?: string, replace?: boolean) {
|
||||
const scoped = this.context as IScopedContext;
|
||||
if (subPath) {
|
||||
return scoped.send(subPath, values);
|
||||
}
|
||||
|
||||
return super.receive(values);
|
||||
return super.receive(values, subPath, replace);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -804,7 +816,7 @@ export class ServiceRenderer extends Service {
|
||||
scoped.reload(target, data);
|
||||
}
|
||||
|
||||
setData(values: object) {
|
||||
return this.props.store.updateData(values);
|
||||
setData(values: object, replace?: boolean) {
|
||||
return this.props.store.updateData(values, undefined, replace);
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +376,11 @@ export type Table2RendererEvent =
|
||||
| 'columnToggled'
|
||||
| 'dragOver';
|
||||
|
||||
export type Table2RendererAction = 'selectAll' | 'clearAll' | 'select';
|
||||
export type Table2RendererAction =
|
||||
| 'selectAll'
|
||||
| 'clearAll'
|
||||
| 'select'
|
||||
| 'expand';
|
||||
|
||||
export interface Table2Props extends RendererProps {
|
||||
title?: string;
|
||||
@ -408,7 +412,7 @@ export default class Table2 extends React.Component<Table2Props, object> {
|
||||
static contextType = ScopedContext;
|
||||
|
||||
renderedToolbars: Array<string> = [];
|
||||
control: any;
|
||||
tableRef?: any;
|
||||
|
||||
constructor(props: Table2Props, context: IScopedContext) {
|
||||
super(props);
|
||||
@ -438,16 +442,6 @@ export default class Table2 extends React.Component<Table2Props, object> {
|
||||
scoped.unRegisterComponent(this);
|
||||
}
|
||||
|
||||
@autobind
|
||||
controlRef(control: any) {
|
||||
// 因为 control 有可能被 n 层 hoc 包裹。
|
||||
while (control && control.getWrappedInstance) {
|
||||
control = control.getWrappedInstance();
|
||||
}
|
||||
|
||||
this.control = control;
|
||||
}
|
||||
|
||||
syncSelected() {
|
||||
const {store, onSelect} = this.props;
|
||||
|
||||
@ -934,7 +928,7 @@ export default class Table2 extends React.Component<Table2Props, object> {
|
||||
reload && this.reloadTarget(reload, data);
|
||||
})
|
||||
.catch(() => {
|
||||
options?.resetOnFailed && this.control.reset();
|
||||
options?.resetOnFailed && this.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1199,10 +1193,11 @@ export default class Table2 extends React.Component<Table2Props, object> {
|
||||
}
|
||||
|
||||
doAction(action: ActionObject, args: any, throwErrors: boolean): any {
|
||||
const {store, rowSelection, data} = this.props;
|
||||
const {store, rowSelection, data, keyField: key, expandable} = this.props;
|
||||
|
||||
const actionType = action?.actionType as string;
|
||||
const keyField = rowSelection?.keyField;
|
||||
const keyField = rowSelection?.keyField || key || 'key';
|
||||
const dataSource = store.getData(data).items || [];
|
||||
|
||||
switch (actionType) {
|
||||
case 'selectAll':
|
||||
@ -1212,24 +1207,71 @@ export default class Table2 extends React.Component<Table2Props, object> {
|
||||
store.updateSelected([], keyField);
|
||||
break;
|
||||
case 'select':
|
||||
const dataSource = store.getData(data);
|
||||
const selected: Array<any> = [];
|
||||
dataSource.items.forEach((item: any, rowIndex: number) => {
|
||||
dataSource.forEach((item: any, rowIndex: number) => {
|
||||
const flag = evalExpression(args?.selectedRowKeysExpr, {
|
||||
record: item,
|
||||
rowIndex
|
||||
});
|
||||
if (flag && keyField) {
|
||||
if (flag) {
|
||||
selected.push(item[keyField]);
|
||||
}
|
||||
});
|
||||
store.updateSelected(selected, keyField);
|
||||
break;
|
||||
case 'expand':
|
||||
const expandableKey = expandable?.keyField || key || 'key';
|
||||
const expanded: Array<any> = [];
|
||||
const collapse: Array<any> = [];
|
||||
// value值控制展开1个
|
||||
if (args?.value) {
|
||||
const rowIndex = dataSource.findIndex(
|
||||
(d: any) => d[expandableKey] === args.value
|
||||
);
|
||||
const item = dataSource[rowIndex];
|
||||
if (this.tableRef && this.tableRef.isExpandableRow(item, rowIndex)) {
|
||||
if (this.tableRef.isExpanded(item)) {
|
||||
collapse.push(item);
|
||||
} else {
|
||||
expanded.push(item);
|
||||
}
|
||||
}
|
||||
} else if (args?.expandedRowsExpr) {
|
||||
dataSource.forEach((item: any, rowIndex: number) => {
|
||||
const flag = evalExpression(args?.expandedRowsExpr, {
|
||||
record: item,
|
||||
rowIndex
|
||||
});
|
||||
if (
|
||||
flag &&
|
||||
this.tableRef &&
|
||||
this.tableRef.isExpandableRow(item, rowIndex)
|
||||
) {
|
||||
if (this.tableRef.isExpanded(item)) {
|
||||
collapse.push(item);
|
||||
} else {
|
||||
expanded.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (expanded.length > 0) {
|
||||
this.tableRef && this.tableRef.onExpandRows(expanded);
|
||||
}
|
||||
if (collapse.length > 0) {
|
||||
this.tableRef && this.tableRef.onCollapseRows(collapse);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
getRef(ref: any) {
|
||||
this.tableRef = ref;
|
||||
}
|
||||
|
||||
renderTable() {
|
||||
const {
|
||||
render,
|
||||
@ -1393,6 +1435,7 @@ export default class Table2 extends React.Component<Table2Props, object> {
|
||||
return (
|
||||
<Table
|
||||
{...rest}
|
||||
onRef={this.getRef}
|
||||
title={this.renderSchema('title', title, {data: this.props.data})}
|
||||
footer={this.renderSchema('footer', footer, {data: this.props.data})}
|
||||
columns={this.buildColumns(store.filteredColumns)}
|
||||
|
@ -423,9 +423,15 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
throw new Error('Please implements this!');
|
||||
}
|
||||
|
||||
reload(subPath?: string, query?: any, ctx?: any) {
|
||||
reload(
|
||||
subPath?: string,
|
||||
query?: any,
|
||||
ctx?: any,
|
||||
silent?: boolean,
|
||||
replace?: boolean
|
||||
) {
|
||||
if (query) {
|
||||
return this.receive(query);
|
||||
return this.receive(query, undefined, replace);
|
||||
}
|
||||
|
||||
const {
|
||||
@ -486,10 +492,10 @@ export default class Wizard extends React.Component<WizardProps, WizardState> {
|
||||
}
|
||||
}
|
||||
|
||||
receive(values: object) {
|
||||
receive(values: object, subPath?: string, replace?: boolean) {
|
||||
const {store} = this.props;
|
||||
|
||||
store.updateData(values);
|
||||
store.updateData(values, undefined, replace);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@ -1243,7 +1249,7 @@ export class WizardRenderer extends Wizard {
|
||||
}
|
||||
}
|
||||
|
||||
setData(values: object) {
|
||||
return this.props.store.updateData(values);
|
||||
setData(values: object, replace?: boolean) {
|
||||
return this.props.store.updateData(values, undefined, replace);
|
||||
}
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ main().catch(e => {
|
||||
|
||||
const sourceFile = node.getSourceFile();
|
||||
const position = sourceFile.getLineAndCharacterOfPosition(node.pos);
|
||||
console.log(sourceFile, position);
|
||||
|
||||
console.log(
|
||||
`\x1b[36m${sourceFile.fileName}:${position.line + 1}:${
|
||||
position.character + 1
|
||||
|
Loading…
Reference in New Issue
Block a user