feat: CRUD支持列排序, 搜索区支持选择列字段; Table支持行角标 (#2823)

Co-authored-by: lurunze1226 <lurunze@baidu.com>
This commit is contained in:
RUNZE LU 2021-11-05 17:32:19 +08:00 committed by GitHub
parent 94eb752e43
commit 81ae47b92d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1748 additions and 447 deletions

View File

@ -519,6 +519,78 @@ Cards 模式支持 [Cards](./cards) 中的所有功能。
如果想前端实现过滤功能,请看[前端一次性加载](#前端一次性加载)部分。
### 自动生成查询区域
通过设置`"autoGeneratedFilter": true`开启查询区域,会根据列元素的 `searchable` 属性值,自动生成查询条件表单,只有 `searchable` 属性值为合法的组件 Schema 时才会生成查询条件。注意这个属性和 `filter` 冲突,开启 `filter``autoGeneratedFilter` 将会失效。
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"syncLocation": false,
"autoGenerateFilter": true,
"columns": [
{
"name": "id",
"label": "ID",
"searchable": {
"type": "input-text",
"name": "id",
"label": "主键",
"placeholder": "输入id"
}
},
{
"name": "engine",
"label": "Rendering engine"
},
{
"name": "browser",
"label": "Browser",
"searchable": {
"type": "select",
"name": "browser",
"label": "浏览器",
"placeholder": "选择浏览器",
"options": [
{
"label": "Internet Explorer ",
"value": "ie"
},
{
"label": "AOL browser",
"value": "aol"
},
{
"label": "Firefox",
"value": "firefox"
}
]
}
},
{
"name": "platform",
"label": "Platform(s)"
},
{
"name": "version",
"label": "Engine version",
"searchable": {
"type": "input-number",
"name": "version",
"label": "版本号",
"placeholder": "输入版本号",
"mode": "horizontal"
}
},
{
"name": "grade",
"label": "CSS grade"
}
]
}
```
## 配置默认请求参数
可以配置`defaultParams`,来指定拉取接口时的默认参数:
@ -1960,6 +2032,51 @@ crud 组件支持通过配置`headerToolbar`和`footerToolbar`属性,实现在
这样就只会发送 ids 了。
### 列排序
通过配置`headerToolbar` 中 `columns-toggler``"draggable": true`可以实现设置显示列和列排序功能。
```schema: scope="body"
{
"type": "crud",
"api": "/api/sample",
"syncLocation": false,
"headerToolbar": [
{
"type": "columns-toggler",
"align": "right",
"draggable": true
}
],
"columns": [
{
"name": "id",
"label": "ID"
},
{
"name": "engine",
"label": "Rendering engine"
},
{
"name": "browser",
"label": "Browser"
},
{
"name": "platform",
"label": "Platform(s)"
},
{
"name": "version",
"label": "Engine version"
},
{
"name": "grade",
"label": "CSS grade"
}
]
}
```
## 单条操作
当操作对象是单条数据时这类操作叫单条操作比如编辑、删除、通过、拒绝等等。CRUD 的 table 模式可以在 column 通过放置按钮来完成(其他模式参考 table 模式)。比如编辑就是添加个按钮行为是弹框类型的按钮或者添加一个页面跳转类型的按钮把当前行数据的 id 放在 query 中传过去、删除操作就是配置一个按钮行为是 AJAX 类型的按钮,将数据通过 api 发送给后端完成。
@ -2290,6 +2407,7 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
| footerToolbar | Array | `['statistics', 'pagination']` | 底部工具栏配置 |
| alwaysShowPagination | `boolean` | `false` | 是否总是显示分页 |
| affixHeader | `boolean` | `true` | 是否固定表头(table 下) |
| autoGeneratedFilter | `boolean` | `false` | 是否开启查询区域,开启后会根据列元素的 `searchable` 属性值,自动生成查询条件表单 |
注意除了上面这些属性CRUD 在不同模式下的属性需要参考各自的文档,比如

View File

@ -1613,6 +1613,140 @@ order: 67
注意这个属性和 `checkOnItemClick` 冲突,因为都是定义行的点击行为,开启 `itemAction``checkOnItemClick` 将会失效。
## 行角标
> 1.4.0 及以上版本
通过属性`itemBadge`,可以为表格行配置[角标](./badge),可以使用[数据映射](../../../docs/concepts/data-mapping)为每一行添加特定的 Badge 属性。[`visibleOn`](../../../docs/concepts/expression)属性控制显示的条件,表达式中`this`可以取到行所在上下文的数据,比如行数据中有`badgeText`字段才显示角标,可以设置`"visibleOn": "this.badgeText"`
```schema: scope="body"
{
"type": "service",
"body": {
"type": "table",
"source": "${table}",
"syncLocation": false,
"itemBadge": {
"text": "${badgeText}",
"mode": "ribbon",
"position": "top-left",
"level": "${badgeLevel}",
"visibleOn": "this.badgeText"
},
"columns": [
{
"name": "id",
"label": "ID",
"searchable": {
"type": "input-text",
"name": "id",
"label": "主键",
"placeholder": "输入id",
"size": "sm",
}
},
{
"name": "engine",
"label": "Rendering engine"
},
{
"name": "browser",
"label": "Browser",
"searchable": {
"type": "select",
"name": "browser",
"label": "浏览器",
"placeholder": "选择浏览器",
"size": "sm",
"options": [
{
"label": "Internet Explorer ",
"value": "ie"
},
{
"label": "AOL browser",
"value": "aol"
},
{
"label": "Firefox",
"value": "firefox"
}
]
}
},
{
"name": "platform",
"label": "Platform(s)"
},
{
"name": "version",
"label": "Engine version",
"searchable": {
"type": "input-number",
"name": "version",
"label": "版本号",
"placeholder": "输入版本号",
"size": "sm",
"mode": "horizontal"
}
},
{
"name": "grade",
"label": "CSS grade"
}
]
},
data: {
table: [
{
"id": 1,
"engine": "Trident",
"browser": "Internet Explorer 4.0",
"platform": "Win 95+",
"version": "4",
"grade": "X",
"badgeText": "默认",
"badgeLevel": "info"
},
{
"id": 2,
"engine": "Trident",
"browser": "Internet Explorer 5.0",
"platform": "Win 95+",
"version": "5",
"grade": "C",
"badgeText": "危险",
"badgeLevel": "danger"
},
{
"id": 3,
"engine": "Trident",
"browser": "Internet Explorer 5.5",
"platform": "Win 95+",
"version": "5.5",
"grade": "A"
},
{
"id": 4,
"engine": "Trident",
"browser": "Internet Explorer 6",
"platform": "Win 98+",
"version": "6",
"grade": "A"
},
{
"id": 5,
"engine": "Trident",
"browser": "Internet Explorer 7",
"platform": "Win XP SP2+",
"version": "7",
"grade": "A"
}
]
}
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
@ -1638,6 +1772,7 @@ order: 67
| rowClassNameExpr | [模板](../../docs/concepts/template) | | 通过模板给行添加 CSS 类名 |
| prefixRow | `Array` | | 顶部总结行 |
| affixRow | `Array` | | 底部总结行 |
| itemBadge | [`BadgeSchema`](./badge) | | 行角标配置 |
## 列配置属性表
@ -1645,11 +1780,11 @@ order: 67
| ---------- | --------------------------------------------- | ------- | ---------------- |
| label | [模板](../../docs/concepts/template) | | 表头文本内容 |
| name | `string` | | 通过名称关联数据 |
| fixed | `left`/`right`/`none` | | 是否固定当前列 |
| fixed | `left` \| `right` \| `none` | | 是否固定当前列 |
| popOver | | | 弹出框 |
| quickEdit | | | 快速编辑 |
| copyable | `boolean``{icon: string, content:string}` | | 是否可复制 |
| sortable | `boolean` | `false` | 是否可排序 |
| searchable | `boolean` | `false` | 是否可快速搜索 |
| width | `number`/`string` | 列宽 |
| searchable | `boolean` \| `Schema` | `false` | 是否可快速搜索 |
| width | `number` \| `string` | 列宽 |
| remark | | | 提示信息 |

View File

@ -1,405 +1,407 @@
module.exports = [
{
"engine": "Trident",
"browser": "Internet Explorer 4.0",
"platform": "Win 95+",
"version": "4",
"grade": "X"
},
{
"engine": "Trident",
"browser": "Internet Explorer 5.0",
"platform": "Win 95+",
"version": "5",
"grade": "C"
},
{
"engine": "Trident",
"browser": "Internet Explorer 5.5",
"platform": "Win 95+",
"version": "5.5",
"grade": "A"
},
{
"engine": "Trident",
"browser": "Internet Explorer 6",
"platform": "Win 98+",
"version": "6",
"grade": "A"
},
{
"engine": "Trident",
"browser": "Internet Explorer 7",
"platform": "Win XP SP2+",
"version": "7",
"grade": "A"
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win XP",
"version": "6",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 1.5",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 3.0",
"platform": "Win 2k+ / OSX.3+",
"version": "1.9",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Camino 1.0",
"platform": "OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Camino 1.5",
"platform": "OSX.3+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Netscape 7.2",
"platform": "Win 95+ / Mac OS 8.6-9.2",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Netscape Browser 8",
"platform": "Win 98SE+",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Netscape Navigator 9",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.0",
"platform": "Win 95+ / OSX.1+",
"version": "1",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.1",
"platform": "Win 95+ / OSX.1+",
"version": "1.1",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.2",
"platform": "Win 95+ / OSX.1+",
"version": "1.2",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.3",
"platform": "Win 95+ / OSX.1+",
"version": "1.3",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.4",
"platform": "Win 95+ / OSX.1+",
"version": "1.4",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.5",
"platform": "Win 95+ / OSX.1+",
"version": "1.5",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.6",
"platform": "Win 95+ / OSX.1+",
"version": "1.6",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.7",
"platform": "Win 98+ / OSX.1+",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.8",
"platform": "Win 98+ / OSX.1+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Seamonkey 1.1",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Epiphany 2.20",
"platform": "Gnome",
"version": "1.8",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 1.2",
"platform": "OSX.3",
"version": "125.5",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 1.3",
"platform": "OSX.3",
"version": "312.8",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 2.0",
"platform": "OSX.4+",
"version": "419.3",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 3.0",
"platform": "OSX.4+",
"version": "522.1",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "OmniWeb 5.5",
"platform": "OSX.4+",
"version": "420",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "iPod Touch / iPhone",
"platform": "iPod",
"version": "420.1",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "S60",
"platform": "S60",
"version": "413",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 7.0",
"platform": "Win 95+ / OSX.1+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 7.5",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 8.0",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 8.5",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 9.0",
"platform": "Win 95+ / OSX.3+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 9.2",
"platform": "Win 88+ / OSX.3+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 9.5",
"platform": "Win 88+ / OSX.3+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera for Wii",
"platform": "Wii",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Nokia N800",
"platform": "N800",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Nintendo DS browser",
"platform": "Nintendo DS",
"version": "8.5",
"grade": "C"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.1",
"platform": "KDE 3.1",
"version": "3.1",
"grade": "C"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.3",
"platform": "KDE 3.3",
"version": "3.3",
"grade": "A"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.5",
"platform": "KDE 3.5",
"version": "3.5",
"grade": "A"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 4.5",
"platform": "Mac OS 8-9",
"version": "-",
"grade": "X"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 5.1",
"platform": "Mac OS 7.6-9",
"version": "1",
"grade": "C"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 5.2",
"platform": "Mac OS 8-X",
"version": "1",
"grade": "C"
},
{
"engine": "Misc",
"browser": "NetFront 3.1",
"platform": "Embedded devices",
"version": "-",
"grade": "C"
},
{
"engine": "Misc",
"browser": "NetFront 3.4",
"platform": "Embedded devices",
"version": "-",
"grade": "A"
},
{
"engine": "Misc",
"browser": "Dillo 0.8",
"platform": "Embedded devices",
"version": "-",
"grade": "X"
},
{
"engine": "Misc",
"browser": "Links",
"platform": "Text only",
"version": "-",
"grade": "X"
},
{
"engine": "Misc",
"browser": "Lynx",
"platform": "Text only",
"version": "-",
"grade": "X"
},
{
"engine": "Misc",
"browser": "IE Mobile",
"platform": "Windows Mobile 6",
"version": "-",
"grade": "C"
},
{
"engine": "Misc",
"browser": "PSP browser",
"platform": "PSP",
"version": "-",
"grade": "C"
},
{
"engine": "Other browsers",
"browser": "All others",
"platform": "-",
"version": "-",
"grade": "U"
}
].map(function (item, index) {
return Object.assign({}, item, {
id: index + 1
})
});
{
"engine": "Trident",
"browser": "Internet Explorer 4.0",
"platform": "Win 95+",
"version": "4",
"grade": "X",
"badgeText": "默认"
},
{
"engine": "Trident",
"browser": "Internet Explorer 5.0",
"platform": "Win 95+",
"version": "5",
"grade": "C",
"badgeText": "危险"
},
{
"engine": "Trident",
"browser": "Internet Explorer 5.5",
"platform": "Win 95+",
"version": "5.5",
"grade": "A"
},
{
"engine": "Trident",
"browser": "Internet Explorer 6",
"platform": "Win 98+",
"version": "6",
"grade": "A"
},
{
"engine": "Trident",
"browser": "Internet Explorer 7",
"platform": "Win XP SP2+",
"version": "7",
"grade": "A"
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win XP",
"version": "6",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 1.5",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Firefox 3.0",
"platform": "Win 2k+ / OSX.3+",
"version": "1.9",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Camino 1.0",
"platform": "OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Camino 1.5",
"platform": "OSX.3+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Netscape 7.2",
"platform": "Win 95+ / Mac OS 8.6-9.2",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Netscape Browser 8",
"platform": "Win 98SE+",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Netscape Navigator 9",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.0",
"platform": "Win 95+ / OSX.1+",
"version": "1",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.1",
"platform": "Win 95+ / OSX.1+",
"version": "1.1",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.2",
"platform": "Win 95+ / OSX.1+",
"version": "1.2",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.3",
"platform": "Win 95+ / OSX.1+",
"version": "1.3",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.4",
"platform": "Win 95+ / OSX.1+",
"version": "1.4",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.5",
"platform": "Win 95+ / OSX.1+",
"version": "1.5",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.6",
"platform": "Win 95+ / OSX.1+",
"version": "1.6",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.7",
"platform": "Win 98+ / OSX.1+",
"version": "1.7",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.8",
"platform": "Win 98+ / OSX.1+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Seamonkey 1.1",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A"
},
{
"engine": "Gecko",
"browser": "Epiphany 2.20",
"platform": "Gnome",
"version": "1.8",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 1.2",
"platform": "OSX.3",
"version": "125.5",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 1.3",
"platform": "OSX.3",
"version": "312.8",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 2.0",
"platform": "OSX.4+",
"version": "419.3",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "Safari 3.0",
"platform": "OSX.4+",
"version": "522.1",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "OmniWeb 5.5",
"platform": "OSX.4+",
"version": "420",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "iPod Touch / iPhone",
"platform": "iPod",
"version": "420.1",
"grade": "A"
},
{
"engine": "Webkit",
"browser": "S60",
"platform": "S60",
"version": "413",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 7.0",
"platform": "Win 95+ / OSX.1+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 7.5",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 8.0",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 8.5",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 9.0",
"platform": "Win 95+ / OSX.3+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 9.2",
"platform": "Win 88+ / OSX.3+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera 9.5",
"platform": "Win 88+ / OSX.3+",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Opera for Wii",
"platform": "Wii",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Nokia N800",
"platform": "N800",
"version": "-",
"grade": "A"
},
{
"engine": "Presto",
"browser": "Nintendo DS browser",
"platform": "Nintendo DS",
"version": "8.5",
"grade": "C"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.1",
"platform": "KDE 3.1",
"version": "3.1",
"grade": "C"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.3",
"platform": "KDE 3.3",
"version": "3.3",
"grade": "A"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.5",
"platform": "KDE 3.5",
"version": "3.5",
"grade": "A"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 4.5",
"platform": "Mac OS 8-9",
"version": "-",
"grade": "X"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 5.1",
"platform": "Mac OS 7.6-9",
"version": "1",
"grade": "C"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 5.2",
"platform": "Mac OS 8-X",
"version": "1",
"grade": "C"
},
{
"engine": "Misc",
"browser": "NetFront 3.1",
"platform": "Embedded devices",
"version": "-",
"grade": "C"
},
{
"engine": "Misc",
"browser": "NetFront 3.4",
"platform": "Embedded devices",
"version": "-",
"grade": "A"
},
{
"engine": "Misc",
"browser": "Dillo 0.8",
"platform": "Embedded devices",
"version": "-",
"grade": "X"
},
{
"engine": "Misc",
"browser": "Links",
"platform": "Text only",
"version": "-",
"grade": "X"
},
{
"engine": "Misc",
"browser": "Lynx",
"platform": "Text only",
"version": "-",
"grade": "X"
},
{
"engine": "Misc",
"browser": "IE Mobile",
"platform": "Windows Mobile 6",
"version": "-",
"grade": "C"
},
{
"engine": "Misc",
"browser": "PSP browser",
"platform": "PSP",
"version": "-",
"grade": "C"
},
{
"engine": "Other browsers",
"browser": "All others",
"platform": "-",
"version": "-",
"grade": "U"
}
].map(function (item, index) {
return Object.assign({}, item, {
id: index + 1
})
});

View File

@ -1178,6 +1178,8 @@
--Table-toolbar-marginX: 0;
--Table-toolbar-marginY: var(--gap-base);
--Table-tree-borderColor: var(--Table-borderColor);
--Table-searchableForm-backgroundColor: #f6f7f8;
--Table-searchableForm-borderRadius: #{px2rem(4px)};
--TableCell--edge-paddingX: var(--gap-md);
--TableCell-filterBtn--onActive-color: var(--primary);
@ -1378,4 +1380,11 @@
--Steps-line-success-bg: var(--Steps-status-success);
--Progress-borderRadius: var(--borderRadius);
--ColumnToggler-backgroundColor: var(--white);
--ColumnToggler-borderRadius: #{px2rem(4px)};
--ColumnToggler-lineHeight: #{px2rem(24px)};
--ColumnToggler-title-fontColor: #080e1a;
--ColumnToggler-fontColor: #151a26;
--ColumnToggler-item-backgroundColor: #f6f7f8;
--ColumnToggler-item-backgroundColor-onHover: rgba(36, 104, 242, 0.1);
}

View File

@ -0,0 +1,234 @@
.#{$ns}ColumnToggler {
position: relative;
display: inline-block;
&-caret {
margin-left: var(--DropDown-caret-marginLeft);
display: inline-block;
vertical-align: top;
transition: transform var(--animation-duration) ease;
> svg {
width: px2rem(10px);
height: px2rem(10px);
top: 0.125em;
}
}
&.is-opened &-caret {
transform: rotate(180deg);
}
&.is-actived > .#{$ns}Button {
color: var(--link-color);
}
&--block {
display: block;
.#{$ns}Button {
display: block;
}
}
&-menu {
position: absolute;
z-index: $zindex-dropdown;
top: 100%;
left: 0;
margin: px2rem(1px) 0 0;
background: var(--DropDown-menu-bg);
list-style: none;
padding: var(--DropDown-menu-paddingY) var(--DropDown-menu-paddingX);
border: var(--DropDown-menu-borderWidth) solid
var(--DropDown-menu-borderColor);
border-radius: var(--DropDown-menu-borderRadius);
box-shadow: var(--DropDown-menu-boxShadow);
min-width: var(--DropDown-menu-minWidth);
text-align: left;
}
&--alignRight &-menu {
left: auto;
right: 0;
}
&-menuItem,
&-menu > li {
padding: var(--DropDown-menuItem-paddingY) var(--DropDown-menuItem-paddingX);
white-space: nowrap;
box-sizing: border-box;
height: var(--DropDown-menu-height);
vertical-align: middle;
user-select: none;
color: var(--link-color);
text-decoration: var(--link-decoration);
&:hover {
background: var(--DropDown-menuItem-onHover-bg);
color: var(--DropDown-menuItem-onHover-color);
}
&.is-active {
color: var(--DropDown-menuItem-onActive-color);
}
&:not(.is-disabled),
&:not(.disabled) {
cursor: pointer;
}
&.is-disabled {
cursor: not-allowed;
// pointer-events: none;
color: var(--DropDown-menuItem-onDisabled-color);
filter: grayscale(80%);
}
&.#{$ns}DropDown-divider {
height: px2rem(1px);
margin: px2rem(9px) 0;
overflow: hidden;
background: var(--DropDown-menu-borderColor);
padding: 0;
}
}
&-menu > li a {
color: inherit;
display: block;
text-decoration: none;
}
&-popover {
border: none;
box-shadow: none;
}
> .#{$ns}Button {
min-width: unset;
}
@mixin flex-layout($direction: row) {
display: flex;
flex-flow: $direction nowrap;
justify-content: space-between;
align-items: center;
}
&-modal {
@include flex-layout(column);
width: px2rem(400px);
padding: var(--ColumnToggler-lineHeight);
margin-top: calc(50vh - 100px);
border-radius: var(--ColumnToggler-borderRadius);
background: var(--ColumnToggler-backgroundColor);
box-shadow: 0 4px 5px 0 rgba(21, 26, 38, 0.06),
0 1px 10px 0 rgba(21, 26, 38, 0.05), 0 2px 4px -1px rgba(21, 26, 38, 0.04);
&-header {
width: 100%;
@include flex-layout();
a,
span {
display: inline-block;
}
.#{$ns}ColumnToggler-modal-title {
opacity: 0.95;
font-size: var(--fontSizeMd);
color: var(--ColumnToggler-title-fontColor);
line-height: var(--ColumnToggler-lineHeight);
font-weight: bold;
}
}
&-content {
padding: 0;
width: 100%;
list-style: none;
margin: px2rem(8px) 0;
.#{$ns}ColumnToggler-menuItem {
color: var(--ColumnToggler-title-fontColor);
background: var(--ColumnToggler-item-backgroundColor);
border-radius: px2rem(2px);
font-size: var(--fontSizeSm);
padding: px2rem(4px) px2rem(8px);
height: var(--ColumnToggler-lineHeight);
width: calc((100% - 24px) / 3);
float: left;
margin: px2rem(4px);
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
&:hover {
background: var(--ColumnToggler-item-backgroundColor-onHover);
}
&--dragging {
border: 1px solid var(--link-color);
}
label {
> i {
height: var(--fontSizeLg);
width: var(--fontSizeLg);
vertical-align: top;
}
}
&-dragBar {
margin-left: 0;
margin-right: var(--gap-xs);
cursor: move;
color: #d8d8d8;
}
}
}
&-footer {
width: 100%;
@include flex-layout();
.#{$ns}ColumnToggler-modeSelect {
color: var(--ColumnToggler-fontColor);
font-size: var(--fontSizeSm);
&.is-actived {
color: var(--link-color);
}
}
& > div {
@include flex-layout();
&:first-child {
justify-content: flex-start;
a {
display: inline-block;
}
}
&:last-child {
justify-content: flex-end;
button {
width: 72px;
}
}
}
}
}
}
.#{$ns}ColumnToggler-tooltip {
border: none;
.#{$ns}Tooltip-arrow::before {
border-top-color: transparent;
}
}

View File

@ -136,6 +136,22 @@
}
}
&-searchableForm {
background: var(--Table-searchableForm-backgroundColor);
border-radius: var(--Table-searchableForm-borderRadius);
&-footer {
padding: var(--Panel-footerPadding);
clear: both;
}
&-checkbox {
& > div > .#{$ns}CheckboxControl {
padding-top: 0;
}
}
}
&-header {
padding: var(--Table-toolbar-marginY) var(--Table-toolbar-marginX);
@ -317,6 +333,8 @@
}
> tbody > tr {
position: relative;
& + tr {
border-top: var(--Table-borderWidth) solid var(--Table-borderColor);
> th {
@ -867,6 +885,12 @@
visibility: hidden;
position: absolute;
}
&-badge {
position: absolute;
top: 0;
left: 0;
}
}
.#{$ns}InputTable-toolbar {

View File

@ -42,6 +42,7 @@
@import '../components/wizard';
@import '../components/crud';
@import '../components/table';
@import '../components/column-toggler';
@import '../components/list';
@import '../components/cards';
@import '../components/card';

View File

@ -13,7 +13,7 @@ import {ClassNamesFn} from '../theme';
* Badge
* https://baidu.gitee.io/amis/docs/components/badge
*/
export interface BadgeSchema extends BaseSchema {
export interface BadgeSchema extends Omit<BaseSchema, 'type'> {
/**
*
*/
@ -64,7 +64,7 @@ export interface BadgeSchema extends BaseSchema {
/**
*
*/
level?: 'info' | 'warning' | 'success' | 'danger';
level?: 'info' | 'warning' | 'success' | 'danger' | SchemaExpression;
}
export interface BadgeProps {
@ -89,17 +89,13 @@ export class Badge extends React.Component<BadgeProps, object> {
animationElement: any
) {
const {classnames: cx, badge, data} = this.props;
let {
mode = 'dot',
level = 'danger',
style
} = badge as BadgeSchema;
let {mode = 'dot', level = 'danger', style} = badge as BadgeSchema;
if (typeof level === 'string' && level[0] === '$') {
level = resolveVariable(level, data);
}
switch(mode) {
switch (mode) {
case 'dot':
return (
<span
@ -112,7 +108,11 @@ export class Badge extends React.Component<BadgeProps, object> {
case 'text':
return (
<span
className={cx('Badge-text', `Badge--${position}`, `Badge--${level}`)}
className={cx(
'Badge-text',
`Badge--${position}`,
`Badge--${level}`
)}
style={{...offsetStyle, ...sizeStyle, ...style}}
>
{text}
@ -127,14 +127,17 @@ export class Badge extends React.Component<BadgeProps, object> {
style={{width: outSize, height: outSize}}
>
<span
className={cx('Badge-ribbon', `Badge-ribbon--${position}`, `Badge--${level}`)}
className={cx(
'Badge-ribbon',
`Badge-ribbon--${position}`,
`Badge--${level}`
)}
style={{...sizeStyle, ...style}}
>
{text}
{animationElement}
</span>
</div>
);
default:
return null;
@ -155,6 +158,7 @@ export class Badge extends React.Component<BadgeProps, object> {
let {
mode = 'dot',
text,
level,
size,
style,
offset,
@ -177,7 +181,7 @@ export class Badge extends React.Component<BadgeProps, object> {
if (typeof size === 'undefined') {
if (mode === 'dot') {
size = 6;
} else if (mode === 'ribbon'){
} else if (mode === 'ribbon') {
size = 12;
} else {
size = 16;
@ -194,7 +198,9 @@ export class Badge extends React.Component<BadgeProps, object> {
// 当text、overflowCount都为number类型时进行封顶值处理
if (typeof text === 'number' && typeof overflowCount === 'number') {
text = (
(text as number) > (overflowCount as number) ? `${overflowCount}+` : text
(text as number) > (overflowCount as number)
? `${overflowCount}+`
: text
) as string | number;
}
@ -222,7 +228,7 @@ export class Badge extends React.Component<BadgeProps, object> {
const left = `calc(50% + ${parseInt(offset[0] as string, 10)}px)`;
const right = `calc(-50% + ${parseInt(offset[1] as string, 10)}px)`;
offsetStyle = {
transform: `translate(${left}, ${right})`,
transform: `translate(${left}, ${right})`
};
}
@ -250,15 +256,16 @@ export class Badge extends React.Component<BadgeProps, object> {
return (
<div className={cx('Badge', className)}>
{children}
{isDisplay ?
this.renderBadge(
text,
size,
position,
offsetStyle,
sizeStyle,
animationElement
) : null}
{isDisplay
? this.renderBadge(
text,
size,
position,
offsetStyle,
sizeStyle,
animationElement
)
: null}
</div>
);
}

View File

@ -172,6 +172,8 @@ register('de-DE', {
'Table.startSort': 'Klicken, um Sortierung zu starten',
'Table.valueField': 'valueField muss vorhanden sein',
'Table.index': 'Index',
'Table.toggleColumn': 'Spalten anzeigen',
'Table.searchFields': 'Abfragefelder setzen',
'Tag.placeholder': 'Noch kein Tag',
'Tag.tip': 'Kürzlich verwendetes Tag',
'Text.add': 'Neu {{label}}',

View File

@ -173,6 +173,8 @@ register('en-US', {
'Table.startSort': 'Click to start sorting',
'Table.valueField': 'Must have valueField',
'Table.index': 'Index',
'Table.toggleColumn': 'Display columns',
'Table.searchFields': 'Set query fields',
'Tag.placeholder': 'No tag yet',
'Tag.tip': 'Recently used tag',
'Text.add': 'New {{label}}',

View File

@ -177,6 +177,8 @@ register('zh-CN', {
'Table.startSort': '点击开始排序',
'Table.valueField': '请配置 valueField',
'Table.index': '序号',
'Table.toggleColumn': '显示列',
'Table.searchFields': '设置查询字段',
'Tag.placeholder': '暂无标签',
'Tag.tip': '最近使用的标签',
'Text.add': '新增:{{label}}',

View File

@ -345,6 +345,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
'footerToolbar',
'filterTogglable',
'filterDefaultVisible',
'autoGenerateFilter',
'syncResponse2Query',
'keepItemSelectionOnPageChange',
'labelTpl',
@ -1985,6 +1986,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
popOverContainer,
translate: __,
onQuery,
autoGenerateFilter,
...rest
} = this.props;
@ -2035,6 +2037,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
key: 'body',
className: cx('Crud-body', bodyClassName),
ref: this.controlRef,
autoGenerateFilter: !filter && autoGenerateFilter,
selectable: !!(
(this.hasBulkActionsToolbar() && this.hasBulkActions()) ||
pickerMode
@ -2067,6 +2070,9 @@ export default class CRUD extends React.Component<CRUDProps, any> {
onSelect: this.handleSelect,
onPopOverOpened: this.handleChildPopOverOpen,
onPopOverClosed: this.handleChildPopOverClose,
onSearchableFromReset: this.handleFilterReset,
onSearchableFromSubmit: this.handleFilterSubmit,
onSearchableFromInit: this.handleFilterInit,
headerToolbarRender: this.renderHeaderToolbar,
footerToolbarRender: this.renderFooterToolbar,
data: store.mergedData

View File

@ -2,6 +2,7 @@ import React from 'react';
import {FormItem, FormControlProps, FormBaseControl} from './Item';
import cx from 'classnames';
import Checkbox from '../../components/Checkbox';
import {withBadge, BadgeSchema} from '../../components/Badge';
/**
* Checkbox
@ -27,6 +28,11 @@ export interface CheckboxControlSchema extends FormBaseControl {
*
*/
option?: string;
/**
*
*/
badge?: BadgeSchema;
}
export interface CheckboxProps
@ -78,4 +84,6 @@ export default class CheckboxControl extends React.Component<
type: 'checkbox',
sizeMutable: false
})
// @ts-ignore
@withBadge
export class CheckboxControlRenderer extends CheckboxControl {}

View File

@ -251,7 +251,6 @@ export interface ComboControlSchema extends FormBaseControl {
/**
*
*/
maxLengthValidateFailed?: string;
};
}

View File

@ -0,0 +1,544 @@
import React from 'react';
import {findDOMNode} from 'react-dom';
import Sortable from 'sortablejs';
import cloneDeep from 'lodash/cloneDeep';
import {RendererProps} from '../../factory';
import Overlay from '../../components/Overlay';
import PopOver from '../../components/PopOver';
import Modal from '../../components/Modal';
import Button from '../../components/Button';
import Checkbox from '../../components/Checkbox';
import TooltipWrapper from '../../components/TooltipWrapper';
import type {TooltipObject} from '../../components/TooltipWrapper';
import {noop, autobind} from '../../utils/helper';
import {filter} from '../../utils/tpl';
import {Icon} from '../../components/icons';
import {RootClose} from '../../utils/RootClose';
import {IColumn} from '../../store/table';
export interface ColumnTogglerProps extends RendererProps {
/**
*
*/
label?: string | React.ReactNode;
/**
* hover focus
*/
tooltip?: string | TooltipObject;
/**
*
*/
disabledTip?: string | TooltipObject;
/**
*
*/
closeOnOutside?: boolean;
/**
*
*/
closeOnClick?: boolean;
/**
*
*/
align?: 'left' | 'right';
/**
* ColumnToggler的CSS类名
*/
className?: string;
/**
* CSS类名
*/
btnClassName?: string;
/**
*
*/
size?: 'xs' | 'sm' | 'md' | 'lg';
/**
*
*/
level?: 'info' | 'success' | 'danger' | 'warning' | 'primary' | 'link';
/**
* `display: block`
*/
block?: boolean;
/**
*
*/
draggable?: boolean;
/**
*
*/
defaultIsOpened?: boolean;
/**
*
*/
isActived?: boolean;
/**
* ICON名称
*/
icon?: string | React.ReactNode;
/**
*
*/
iconOnly?: boolean;
/**
* Icon
*/
hideExpandIcon?: boolean;
/**
*
*/
overlay?: boolean;
/**
*
*/
columns: Array<IColumn>;
onColumnToggle: (columns: Array<IColumn>) => void;
modalContainer?: () => HTMLElement;
}
export interface ColumnTogglerState {
isOpened: boolean;
enableSorting: boolean;
tempColumns: any[];
}
export default class ColumnToggler extends React.Component<
ColumnTogglerProps,
ColumnTogglerState
> {
state: ColumnTogglerState = {
isOpened: false,
enableSorting: false,
tempColumns: cloneDeep(this.props.columns)
};
static defaultProps: Pick<
ColumnTogglerProps,
'placement' | 'tooltipTrigger' | 'tooltipRootClose' | 'draggable'
> = {
placement: 'top',
tooltipTrigger: ['hover', 'focus'],
tooltipRootClose: false,
draggable: false
};
target: any;
sortable?: Sortable;
dragRefDOM: HTMLElement;
constructor(props: ColumnTogglerProps) {
super(props);
this.open = this.open.bind(this);
this.close = this.close.bind(this);
this.toggle = this.toggle.bind(this);
this.domRef = this.domRef.bind(this);
this.dragRef = this.dragRef.bind(this);
}
componentDidMount() {
if (this.props.defaultIsOpened) {
this.setState({
isOpened: true
});
}
}
componentWillUnmount() {
this.destroyDragging();
}
domRef(ref: any) {
this.target = ref;
}
toggle(e: React.MouseEvent<any>) {
e.preventDefault();
this.setState({
isOpened: !this.state.isOpened
});
}
open() {
this.setState({
isOpened: true
});
}
close() {
this.setState({
isOpened: false,
enableSorting: false,
tempColumns: cloneDeep(this.props.columns)
});
}
swapColumnPosition(oldIndex: number, newIndex: number) {
const columns = this.state.tempColumns;
columns[oldIndex] = columns.splice(newIndex, 1, columns[oldIndex])[0];
this.setState({tempColumns: columns});
}
updateToggledColumn(
column: IColumn,
index: number,
value: any,
shift?: boolean
) {
const tempColumns = this.state.tempColumns.concat();
tempColumns.splice(index, 1, {
...column,
toggled: value
});
this.setState({tempColumns});
}
@autobind
dragRef(ref: any) {
const {enableSorting} = this.state;
const {draggable} = this.props;
if (enableSorting && draggable && ref) {
this.initDragging();
}
}
initDragging() {
const dom = findDOMNode(this) as HTMLElement;
const ns = this.props.classPrefix;
this.sortable = new Sortable(
dom.querySelector(`.${ns}ColumnToggler-modal-content`) as HTMLElement,
{
group: `ColumnToggler-modal-content`,
animation: 150,
handle: `.${ns}ColumnToggler-menuItem-dragBar`,
ghostClass: `${ns}ColumnToggler-menuItem--dragging`,
onEnd: (e: any) => {
if (e.newIndex === e.oldIndex) {
return;
}
const parent = e.to as HTMLElement;
if (e.oldIndex < parent.childNodes.length - 1) {
parent.insertBefore(e.item, parent.childNodes[e.oldIndex]);
} else {
parent.appendChild(e.item);
}
this.swapColumnPosition(e.oldIndex, e.newIndex);
}
}
);
}
destroyDragging() {
this.sortable && this.sortable.destroy();
}
@autobind
onConfirm() {
const {tempColumns} = this.state;
const {onColumnToggle} = this.props;
onColumnToggle && onColumnToggle([...tempColumns]);
this.setState({
isOpened: false,
enableSorting: false
});
}
renderOuter() {
const {
popOverContainer,
classnames: cx,
classPrefix: ns,
children,
closeOnClick,
closeOnOutside
} = this.props;
const body = (
<RootClose
disabled={!this.state.isOpened}
onRootClose={closeOnOutside !== false ? this.close : noop}
>
{(ref: any) => {
return (
<ul
className={cx('ColumnToggler-menu')}
onClick={closeOnClick ? this.close : noop}
ref={ref}
>
{children}
</ul>
);
}}
</RootClose>
);
if (popOverContainer) {
return (
<Overlay container={popOverContainer} target={() => this.target} show>
<PopOver
overlay
onHide={this.close}
classPrefix={ns}
className={cx('ColumnToggler-popover')}
style={{minWidth: this.target?.offsetWidth}}
>
{body}
</PopOver>
</Overlay>
);
}
return body;
}
renderModal() {
const {
render,
classnames: cx,
classPrefix: ns,
modalContainer,
draggable,
overlay,
translate: __
} = this.props;
const {enableSorting, tempColumns} = this.state;
return (
<>
<Modal
closeOnEsc
onHide={this.close}
show={this.state.isOpened}
contentClassName={cx('ColumnToggler-modal')}
container={modalContainer || this.target}
overlay={typeof overlay === 'boolean' ? overlay : false}
>
<header className={cx('ColumnToggler-modal-header')}>
<span className={cx('ColumnToggler-modal-title')}></span>
<a
data-tooltip={__('Dialog.close')}
data-position="left"
className={cx('Modal-close')}
onClick={this.close}
>
<Icon icon="close" className="icon" />
</a>
</header>
<ul className={cx('ColumnToggler-modal-content')} ref={this.dragRef}>
{Array.isArray(tempColumns)
? tempColumns.map((column, index) => (
<TooltipWrapper
tooltipClassName={cx('ColumnToggler-tooltip')}
placement="top"
tooltip={column.label || ''}
trigger={enableSorting ? 'click' : 'hover'}
key={column.index}
>
<li
className={cx('ColumnToggler-menuItem')}
key={column.index}
>
{enableSorting && draggable && tempColumns.length > 1 ? (
<>
<a className={cx('ColumnToggler-menuItem-dragBar')}>
<Icon icon="drag-bar" className={cx('icon')} />
</a>
<span>
{column.label ? render('tpl', column.label) : null}
</span>
</>
) : (
<Checkbox
size="sm"
classPrefix={ns}
checked={column.toggled}
disabled={!column.toggable || enableSorting}
onChange={this.updateToggledColumn.bind(
this,
column,
index
)}
>
<span>
{column.label ? render('tpl', column.label) : null}
</span>
</Checkbox>
)}
</li>
</TooltipWrapper>
))
: null}
</ul>
<footer className={cx('ColumnToggler-modal-footer')}>
<div>
<Button
className={cx(`ColumnToggler-modeSelect`, {
'is-actived': !enableSorting
})}
onClick={() => this.setState({enableSorting: false})}
level="link"
>
{__('Table.toggleColumn')}
</Button>
<Button
className={cx(`ColumnToggler-modeSelect`, {
'is-actived': enableSorting
})}
onClick={() =>
this.setState(
{enableSorting: true},
() =>
this.state.enableSorting &&
this.props.draggable &&
this.initDragging()
)
}
level="link"
disabled={tempColumns.length < 2}
>
{__('sort')}
</Button>
</div>
<div>
<Button className="mr-4" onClick={this.close}>
{__('cancel')}
</Button>
<Button level="primary" onClick={this.onConfirm}>
{__('confirm')}
</Button>
</div>
</footer>
</Modal>
</>
);
}
render() {
const {
tooltip,
placement,
tooltipContainer,
tooltipTrigger,
tooltipRootClose,
disabledTip,
block,
disabled,
btnDisabled,
btnClassName,
size,
label,
level,
primary,
className,
classnames: cx,
align,
iconOnly,
icon,
isActived,
data,
draggable,
hideExpandIcon
} = this.props;
const button = (
<button
onClick={this.toggle}
disabled={disabled || btnDisabled}
className={cx(
'Button',
btnClassName,
typeof level === 'undefined'
? 'Button--default'
: level
? `Button--${level}`
: '',
{
'Button--block': block,
'Button--primary': primary,
'Button--iconOnly': iconOnly
},
size ? `Button--${size}` : ''
)}
>
{icon ? (
typeof icon === 'string' ? (
<i className={cx(icon, 'm-r-xs')} />
) : (
icon
)
) : null}
{typeof label === 'string' ? filter(label, data) : label}
{hideExpandIcon || draggable ? null : (
<span className={cx('ColumnToggler-caret')}>
<Icon icon="caret" className="icon" />
</span>
)}
</button>
);
return (
<div
className={cx(
'ColumnToggler',
{
'ColumnToggler-block': block,
'ColumnToggler--alignRight': align === 'right',
'is-opened': this.state.isOpened,
'is-actived': isActived
},
className
)}
ref={this.domRef}
>
{draggable ? (
button
) : (
<TooltipWrapper
placement={placement}
tooltip={disabled ? disabledTip : tooltip}
container={tooltipContainer}
trigger={tooltipTrigger}
rootClose={tooltipRootClose}
>
{button}
</TooltipWrapper>
)}
{this.state.isOpened
? draggable
? this.renderModal()
: this.renderOuter()
: null}
</div>
);
}
}

View File

@ -6,11 +6,13 @@ import PopOverable from '../PopOver';
import {observer} from 'mobx-react';
import omit = require('lodash/omit');
import {filter} from '../../utils/tpl';
import {Badge} from '../../components/Badge';
export interface TableCellProps extends RendererProps {
wrapperComponent?: React.ReactType;
column: object;
}
export class TableCell extends React.Component<RendererProps> {
static defaultProps = {
wrapperComponent: 'td'
@ -51,9 +53,12 @@ export class TableCell extends React.Component<RendererProps> {
prefix,
affix,
isHead,
colIndex,
row,
showBadge,
itemBadge,
...rest
} = this.props;
const schema = {
...column,
className: innerClassName,
@ -120,6 +125,16 @@ export class TableCell extends React.Component<RendererProps> {
tabIndex={tabIndex}
onKeyUp={onKeyUp}
>
{showBadge ? (
<Badge
classnames={cx}
badge={{
...itemBadge,
className: cx(`Table-badge`, itemBadge?.className)
}}
data={row.data}
/>
) : null}
{prefix}
{body}
{affix}

View File

@ -5,6 +5,7 @@ import {SchemaNode, Action, Schema} from '../../types';
import forEach from 'lodash/forEach';
import {filter} from '../../utils/tpl';
import DropDownButton from '../DropDownButton';
import './ColumnToggler';
import Checkbox from '../../components/Checkbox';
import Button from '../../components/Button';
import {TableStore, ITableStore, IColumn, IRow} from '../../store/table';
@ -17,7 +18,8 @@ import {
isArrayChildrenModified,
getVariable,
removeHTMLTag,
eachTree
eachTree,
isObject
} from '../../utils/helper';
import {
isPureVariable,
@ -52,6 +54,8 @@ import {TplSchema} from '../Tpl';
import {MappingSchema} from '../Mapping';
import {isAlive, getSnapshot} from 'mobx-state-tree';
import ItemActionsWrapper from './ItemActionsWrapper';
import ColumnToggler from './ColumnToggler';
import {BadgeSchema} from '../../components/Badge';
/**
*
@ -100,7 +104,7 @@ export type TableColumnObject = {
/**
*
*/
searchable?: boolean;
searchable?: boolean | SchemaObject;
/**
*
@ -280,6 +284,16 @@ export interface TableSchema extends BaseSchema {
*
*/
rowClassNameExpr?: string;
/**
*
*/
itemBadge?: BadgeSchema;
/**
* searchable属性值
*/
autoGenerateFilter?: boolean;
}
export interface TableProps extends RendererProps {
@ -347,6 +361,7 @@ export interface TableProps extends RendererProps {
popOverContainer?: any;
canAccessSuperData?: boolean;
reUseRow?: boolean;
itemBadge?: BadgeSchema;
}
type ExportExcelToolbar = SchemaNode & {
@ -406,7 +421,8 @@ export default class Table extends React.Component<TableProps, object> {
'popOverContainer',
'headerToolbarClassName',
'toolbarClassName',
'footerToolbarClassName'
'footerToolbarClassName',
'itemBadge'
];
static defaultProps: Partial<TableProps> = {
className: '',
@ -479,6 +495,8 @@ export default class Table extends React.Component<TableProps, object> {
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
this.subFormRef = this.subFormRef.bind(this);
this.handleColumnToggle = this.handleColumnToggle.bind(this);
this.renderAutoFilterForm = this.renderAutoFilterForm.bind(this);
const {
store,
@ -1348,6 +1366,111 @@ export default class Table extends React.Component<TableProps, object> {
document.removeEventListener('mouseup', this.handleColResizeMouseUp);
}
handleColumnToggle(columns: Array<IColumn>) {
const {store} = this.props;
store.update({columns});
}
renderAutoFilterForm(): React.ReactNode {
const {
render,
store,
onSearchableFromReset,
onSearchableFromSubmit,
onSearchableFromInit,
classnames: cx,
translate: __
} = this.props;
const searchableColumns = store.searchableColumns;
const activedSearchableColumns = store.activedSearchableColumns;
if (!searchableColumns.length) {
return null;
}
const groupedSearchableColumns: Array<Record<string, any>> = [
{body: [], md: 4},
{body: [], md: 4},
{body: [], md: 4}
];
activedSearchableColumns.forEach((column, index) => {
groupedSearchableColumns[index % 3].body.push({
...column.searchable,
name: column.searchable?.name ?? column.name,
label: column.searchable?.label ?? column.label,
mode: 'horizontal'
});
});
return render(
'searchable-form',
{
type: 'form',
api: null,
title: '',
mode: 'normal',
submitText: __('search'),
body: [
{
type: 'grid',
columns: groupedSearchableColumns
}
],
actions: [
{
type: 'dropdown-button',
label: __('Table.searchFields'),
className: cx('Table-searchableForm-dropdown', 'mr-2'),
level: 'link',
trigger: 'click',
size: 'sm',
align: 'right',
buttons: searchableColumns.map(column => {
return {
type: 'checkbox',
className: cx('Table-searchableForm-checkbox'),
name: `__search_${column.searchable?.name ?? column.name}`,
option: column.searchable?.label ?? column.label,
value: column.enableSearch,
badge: {
offset: [-10, 5],
visibleOn: `${
column.toggable && !column.toggled && column.enableSearch
}`
},
onChange: (value: boolean) => {
column.setEnableSearch(value);
}
};
})
},
{
type: 'submit',
label: __('search'),
level: 'primary',
className: 'w-18'
},
{
type: 'reset',
label: __('reset'),
className: 'w-18'
}
]
},
{
key: 'searchable-form',
panelClassName: cx('Table-searchableForm'),
actionsClassName: cx('Table-searchableForm-footer'),
onReset: onSearchableFromReset,
onSubmit: onSearchableFromSubmit,
onInit: onSearchableFromInit,
formStore: undefined
}
);
}
renderHeading() {
let {
title,
@ -1437,7 +1560,8 @@ export default class Table extends React.Component<TableProps, object> {
render,
classPrefix: ns,
resizable,
classnames: cx
classnames: cx,
autoGenerateFilter
} = this.props;
if (column.type === '__checkme') {
@ -1484,7 +1608,7 @@ export default class Table extends React.Component<TableProps, object> {
let affix = null;
if (column.searchable && column.name) {
if (column.searchable && column.name && !autoGenerateFilter) {
affix = (
<HeadCellSearchDropDown
{...this.props}
@ -1637,7 +1761,8 @@ export default class Table extends React.Component<TableProps, object> {
classnames: cx,
checkOnItemClick,
popOverContainer,
canAccessSuperData
canAccessSuperData,
itemBadge
} = this.props;
if (column.name && item.rowSpans[column.name] === 0) {
@ -1725,7 +1850,13 @@ export default class Table extends React.Component<TableProps, object> {
quickEditFormRef: this.subFormRef,
prefix,
onImageEnlarge: this.handleImageEnlarge,
canAccessSuperData
canAccessSuperData,
row: item,
itemBadge,
showBadge:
!props.isHead &&
itemBadge &&
store.firstToggledColumnIndex === props.colIndex
};
delete subProps.label;
@ -1960,23 +2091,29 @@ export default class Table extends React.Component<TableProps, object> {
}
return (
<DropDownButton
<ColumnToggler
{...rest}
tooltip={__('Table.columnsVisibility')}
{...(isObject(config) ? config : {})}
tooltip={config?.tooltip || __('Table.columnsVisibility')}
tooltipContainer={
env && env.getModalContainer ? env.getModalContainer : undefined
}
align={config ? config.align : 'left'}
align={config?.align ?? 'left'}
isActived={store.hasColumnHidden()}
classnames={cx}
classPrefix={ns}
key="columns-toggable"
size="sm"
label={<Icon icon="columns" className="icon m-r-none" />}
size={config?.size || 'sm'}
label={
config?.label || <Icon icon="columns" className="icon m-r-none" />
}
draggable={config?.draggable}
columns={store.columnsData}
onColumnToggle={this.handleColumnToggle}
>
{store.toggableColumns.map(column => (
<li
className={cx('DropDown-menuItem')}
className={cx('ColumnToggler-menuItem')}
key={column.index}
onClick={column.toggleToggle}
>
@ -1985,7 +2122,7 @@ export default class Table extends React.Component<TableProps, object> {
</Checkbox>
</li>
))}
</DropDownButton>
</ColumnToggler>
);
}
@ -2535,7 +2672,13 @@ export default class Table extends React.Component<TableProps, object> {
}
render() {
const {className, store, classnames: cx, affixColumns} = this.props;
const {
className,
store,
classnames: cx,
affixColumns,
autoGenerateFilter
} = this.props;
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了,已经渲染了就不重复渲染了。
const heading = this.renderHeading();
@ -2553,6 +2696,7 @@ export default class Table extends React.Component<TableProps, object> {
'Table--unsaved': !!store.modified || !!store.moved
})}
>
{autoGenerateFilter ? this.renderAutoFilterForm() : null}
{header}
{heading}
<div

View File

@ -45,6 +45,7 @@ export const Column = types
checkdisable: false,
isPrimary: false,
searchable: types.maybe(types.frozen()),
enableSearch: true,
sortable: false,
filterable: types.optional(types.frozen(), undefined),
fixed: '',
@ -66,8 +67,13 @@ export const Column = types
table.persistSaveToggledColumns();
},
setToggled(value: boolean) {
self.toggled = value;
},
setEnableSearch(value: boolean) {
self.enableSearch = value;
}
}));
@ -292,6 +298,10 @@ export const TableStore = iRendererStore
keepItemSelectionOnPageChange: false
})
.views(self => {
function getColumnsExceptBuiltinTypes() {
return self.columns.filter(item => !/^__/.test(item.type));
}
function getForms() {
return self.formsRef.map(item => ({
store: getStoreById(item.id) as IFormStore,
@ -499,11 +509,46 @@ export const TableStore = iRendererStore
});
}
function getFirstToggledColumnIndex() {
const column = self.columns.find(
column => !/^__/.test(column.type) && column.toggled
);
return column == null ? null : column.index;
}
function getSearchableColumns() {
return self.columns.filter(
column => column.searchable && isObject(column.searchable)
);
}
function getActivedSearchableColumns() {
return self.columns.filter(
column =>
column.searchable &&
isObject(column.searchable) &&
column.enableSearch
);
}
return {
get columnsData() {
return getColumnsExceptBuiltinTypes();
},
get forms() {
return getForms();
},
get searchableColumns() {
return getSearchableColumns();
},
get activedSearchableColumns() {
return getSearchableColumns().filter(column => column.enableSearch);
},
get filteredColumns() {
return getFilteredColumns();
},
@ -598,6 +643,10 @@ export const TableStore = iRendererStore
return maxLength === selectedLength;
},
get firstToggledColumnIndex() {
return getFirstToggledColumnIndex();
},
getData,
get columnGroup() {