mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 02:48:55 +08:00
feat: CRUD支持列排序, 搜索区支持选择列字段; Table支持行角标 (#2823)
Co-authored-by: lurunze1226 <lurunze@baidu.com>
This commit is contained in:
parent
94eb752e43
commit
81ae47b92d
@ -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 在不同模式下的属性需要参考各自的文档,比如
|
||||
|
||||
|
@ -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 | | | 提示信息 |
|
||||
|
@ -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
|
||||
})
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
|
234
scss/components/_column-toggler.scss
Normal file
234
scss/components/_column-toggler.scss
Normal 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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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';
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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}}',
|
||||
|
@ -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}}',
|
||||
|
@ -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}}',
|
||||
|
@ -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
|
||||
|
@ -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 {}
|
||||
|
@ -251,7 +251,6 @@ export interface ComboControlSchema extends FormBaseControl {
|
||||
/**
|
||||
* 最大值验证错误提示
|
||||
*/
|
||||
|
||||
maxLengthValidateFailed?: string;
|
||||
};
|
||||
}
|
||||
|
544
src/renderers/Table/ColumnToggler.tsx
Normal file
544
src/renderers/Table/ColumnToggler.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user