mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:48:13 +08:00
feat: 新增maxItemSelectionLength支持单独配置当前页最大勾选数 (#9249)
Co-authored-by: wanglinfang <wanglinfang@baidu.com>
This commit is contained in:
parent
f663be714d
commit
fba3074311
@ -2060,75 +2060,220 @@ interface CRUDMatchFunc {
|
||||
|
||||
**保留条目选择**
|
||||
|
||||
默认分页、搜素后,用户选择条目会被清空,配置`keepItemSelectionOnPageChange`属性后会保留用户选择,可以实现跨页面批量操作。
|
||||
默认分页、搜索后,用户选择条目会被清空,配置`keepItemSelectionOnPageChange`属性后会保留用户选择,可以实现跨页面批量操作。
|
||||
同时可以通过配置`maxKeepItemSelectionLength`属性限制最大勾选数
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "crud",
|
||||
"syncLocation": false,
|
||||
"api": "/api/mock2/sample",
|
||||
"headerToolbar": [
|
||||
"bulkActions"
|
||||
],
|
||||
"keepItemSelectionOnPageChange": true,
|
||||
"maxKeepItemSelectionLength": 4,
|
||||
"bulkActions": [
|
||||
{
|
||||
"label": "批量删除",
|
||||
"actionType": "ajax",
|
||||
"api": "delete:/api/mock2/sample/${ids|raw}",
|
||||
"confirmText": "确定要批量删除?"
|
||||
},
|
||||
{
|
||||
"label": "批量修改",
|
||||
"actionType": "dialog",
|
||||
"dialog": {
|
||||
"title": "批量编辑",
|
||||
"body": {
|
||||
"type": "form",
|
||||
"api": "/api/mock2/sample/bulkUpdate2",
|
||||
"body": [
|
||||
{
|
||||
"type": "hidden",
|
||||
"name": "ids"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
}
|
||||
]
|
||||
}
|
||||
"type": "crud",
|
||||
"syncLocation": false,
|
||||
"api": "/api/mock2/sample",
|
||||
"headerToolbar": [
|
||||
"bulkActions"
|
||||
],
|
||||
"keepItemSelectionOnPageChange": true,
|
||||
"maxKeepItemSelectionLength": 4,
|
||||
"bulkActions": [
|
||||
{
|
||||
"label": "批量删除",
|
||||
"actionType": "ajax",
|
||||
"api": "delete:/api/mock2/sample/${ids|raw}",
|
||||
"confirmText": "确定要批量删除?"
|
||||
},
|
||||
{
|
||||
"label": "批量修改",
|
||||
"actionType": "dialog",
|
||||
"dialog": {
|
||||
"title": "批量编辑",
|
||||
"body": {
|
||||
"type": "form",
|
||||
"api": "/api/mock2/sample/bulkUpdate2",
|
||||
"body": [
|
||||
{
|
||||
"type": "hidden",
|
||||
"name": "ids"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**当前页最大勾选数**
|
||||
|
||||
如果不需要在分页、搜索后,保留用户选择,即不需要配置`keepItemSelectionOnPageChange`,可以通过配置`maxItemSelectionLength`属性限制当前页条目的最大勾选数
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "crud",
|
||||
"syncLocation": false,
|
||||
"api": "/api/mock2/sample",
|
||||
"headerToolbar": [
|
||||
"bulkActions"
|
||||
],
|
||||
"maxItemSelectionLength": 4,
|
||||
"bulkActions": [
|
||||
{
|
||||
"label": "批量删除",
|
||||
"actionType": "ajax",
|
||||
"api": "delete:/api/mock2/sample/${ids|raw}",
|
||||
"confirmText": "确定要批量删除?"
|
||||
},
|
||||
{
|
||||
"label": "批量修改",
|
||||
"actionType": "dialog",
|
||||
"dialog": {
|
||||
"title": "批量编辑",
|
||||
"body": {
|
||||
"type": "form",
|
||||
"api": "/api/mock2/sample/bulkUpdate2",
|
||||
"body": [
|
||||
{
|
||||
"type": "hidden",
|
||||
"name": "ids"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"itemCheckableOn": "this.index !== 2"
|
||||
}
|
||||
```
|
||||
|
||||
`maxItemSelectionLength`也可以和`keepItemSelectionOnPageChange`搭配使用,起到和`maxKeepItemSelectionLength`一样的效果
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "crud",
|
||||
"syncLocation": false,
|
||||
"api": "/api/mock2/sample",
|
||||
"headerToolbar": [
|
||||
"bulkActions"
|
||||
],
|
||||
"keepItemSelectionOnPageChange": true,
|
||||
"maxItemSelectionLength": 4,
|
||||
"bulkActions": [
|
||||
{
|
||||
"label": "批量删除",
|
||||
"actionType": "ajax",
|
||||
"api": "delete:/api/mock2/sample/${ids|raw}",
|
||||
"confirmText": "确定要批量删除?"
|
||||
},
|
||||
{
|
||||
"label": "批量修改",
|
||||
"actionType": "dialog",
|
||||
"dialog": {
|
||||
"title": "批量编辑",
|
||||
"body": {
|
||||
"type": "form",
|
||||
"api": "/api/mock2/sample/bulkUpdate2",
|
||||
"body": [
|
||||
{
|
||||
"type": "hidden",
|
||||
"name": "ids"
|
||||
},
|
||||
{
|
||||
"type": "input-text",
|
||||
"name": "engine",
|
||||
"label": "Engine"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"itemCheckableOn": "this.index !== 2 && this.index !== 5"
|
||||
}
|
||||
```
|
||||
|
||||
@ -3665,8 +3810,10 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
|
||||
| hideQuickSaveBtn | `boolean` | `false` | 隐藏顶部快速保存提示 |
|
||||
| autoJumpToTopOnPagerChange | `boolean` | `false` | 当切分页的时候,是否自动跳顶部。 |
|
||||
| syncResponse2Query | `boolean` | `true` | 将返回数据同步到过滤器上。 |
|
||||
| keepItemSelectionOnPageChange | `boolean` | `true` | 保留条目选择,默认分页、搜素后,用户选择条目会被清空,开启此选项后会保留用户选择,可以实现跨页面批量操作。 |
|
||||
| keepItemSelectionOnPageChange | `boolean` | `true` | 保留条目选择,默认分页、搜索后,用户选择条目会被清空,开启此选项后会保留用户选择,可以实现跨页面批量操作。 |
|
||||
| labelTpl | `string` | | 单条描述模板,`keepItemSelectionOnPageChange`设置为`true`后会把所有已选择条目列出来,此选项可以用来定制条目展示文案。 |
|
||||
| maxKeepItemSelectionLength | `number` | `true` | 和`keepItemSelectionOnPageChange`搭配使用,限制最大勾选数。 |
|
||||
| maxItemSelectionLength | `number` | `true` | 可单独使用限制当前页的最大勾选数,也可以和`keepItemSelectionOnPageChange`搭配使用达到和 maxKeepItemSelectionLength 一样的效果。 |
|
||||
| headerToolbar | Array | `['bulkActions', 'pagination']` | 顶部工具栏配置 |
|
||||
| footerToolbar | Array | `['statistics', 'pagination']` | 底部工具栏配置 |
|
||||
| alwaysShowPagination | `boolean` | `false` | 是否总是显示分页 |
|
||||
|
@ -339,13 +339,10 @@ export const Row = types
|
||||
*/
|
||||
get isCheckAvaiableOnClick(): boolean {
|
||||
const table = getParent(self, self.depth * 2) as ITableStore;
|
||||
const keepItemSelectionOnPageChange =
|
||||
table?.keepItemSelectionOnPageChange;
|
||||
const selectionUpperLimit = table?.maxKeepItemSelectionLength;
|
||||
const selectionUpperLimit = table.getSelectionUpperLimit();
|
||||
|
||||
// 如果未做配置,或者配置不合法直接通过检查
|
||||
if (
|
||||
!keepItemSelectionOnPageChange ||
|
||||
!Number.isInteger(selectionUpperLimit) ||
|
||||
selectionUpperLimit === Infinity
|
||||
) {
|
||||
@ -524,6 +521,7 @@ export const TableStore = iRendererStore
|
||||
formsRef: types.optional(types.array(types.frozen()), []),
|
||||
maxKeepItemSelectionLength: Infinity,
|
||||
keepItemSelectionOnPageChange: false,
|
||||
maxItemSelectionLength: Infinity,
|
||||
// 导出 Excel 按钮的 loading 状态
|
||||
exportExcelLoading: false,
|
||||
searchFormExpanded: false, // 用来控制搜索框是否展开了,那个自动根据 searchable 生成的表单 autoGenerateFilter
|
||||
@ -776,7 +774,19 @@ export const TableStore = iRendererStore
|
||||
return self.columns.filter(column => column.searchable);
|
||||
}
|
||||
|
||||
function getSelectionUpperLimit() {
|
||||
const keep = self.keepItemSelectionOnPageChange;
|
||||
const selectionUpperLimit = keep
|
||||
? self.maxKeepItemSelectionLength !== Infinity
|
||||
? self.maxKeepItemSelectionLength
|
||||
: self.maxItemSelectionLength
|
||||
: self.maxItemSelectionLength;
|
||||
|
||||
return selectionUpperLimit;
|
||||
}
|
||||
|
||||
return {
|
||||
getSelectionUpperLimit,
|
||||
get columnsData() {
|
||||
return getColumnsExceptBuiltinTypes();
|
||||
},
|
||||
@ -819,6 +829,11 @@ export const TableStore = iRendererStore
|
||||
},
|
||||
|
||||
get allChecked(): boolean {
|
||||
const selectionUpperLimit = getSelectionUpperLimit();
|
||||
|
||||
if (selectionUpperLimit !== Infinity) {
|
||||
return (self as ITableStore).isSelectionThresholdReached;
|
||||
}
|
||||
// 只要selectedRows中包含checkableRows中的全部数据,就认为是全选
|
||||
return (self as ITableStore).checkableRows.every(item =>
|
||||
self.selectedRows.includes(item)
|
||||
@ -879,9 +894,9 @@ export const TableStore = iRendererStore
|
||||
/** 已选择item是否达到数量上限 */
|
||||
get isSelectionThresholdReached() {
|
||||
const selectedLength = self.data?.selectedItems?.length;
|
||||
const maxLength = self.maxKeepItemSelectionLength;
|
||||
const maxLength = getSelectionUpperLimit();
|
||||
|
||||
if (!self.data || !self.keepItemSelectionOnPageChange || !maxLength) {
|
||||
if (!self.data || maxLength === Infinity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1073,6 +1088,8 @@ export const TableStore = iRendererStore
|
||||
config.keepItemSelectionOnPageChange !== undefined &&
|
||||
(self.keepItemSelectionOnPageChange =
|
||||
config.keepItemSelectionOnPageChange);
|
||||
config.maxItemSelectionLength !== undefined &&
|
||||
(self.maxItemSelectionLength = config.maxItemSelectionLength);
|
||||
|
||||
config.exportExcelLoading !== undefined &&
|
||||
(self.exportExcelLoading = config.exportExcelLoading);
|
||||
@ -1556,14 +1573,11 @@ export const TableStore = iRendererStore
|
||||
}
|
||||
|
||||
function getSelectedRows() {
|
||||
const maxLength = self.maxKeepItemSelectionLength;
|
||||
const keep = self.keepItemSelectionOnPageChange;
|
||||
|
||||
const maxLength = self.getSelectionUpperLimit();
|
||||
const selectedItems = self.data?.selectedItems;
|
||||
|
||||
if (
|
||||
keep &&
|
||||
maxLength &&
|
||||
maxLength !== Infinity &&
|
||||
selectedItems &&
|
||||
maxLength >= selectedItems.length
|
||||
) {
|
||||
@ -1704,11 +1718,11 @@ export const TableStore = iRendererStore
|
||||
if (!self.data) {
|
||||
return;
|
||||
}
|
||||
const maxLength = self.maxKeepItemSelectionLength;
|
||||
const maxLength = self.getSelectionUpperLimit();
|
||||
const selectedItems = self.data.selectedItems;
|
||||
|
||||
self.selectedRows.map(item => item.setCheckdisable(false));
|
||||
if (maxLength && maxLength <= selectedItems.length) {
|
||||
if (maxLength !== Infinity && maxLength <= selectedItems.length) {
|
||||
self.unSelectedRows.map(
|
||||
item => !item.checked && item.setCheckdisable(true)
|
||||
);
|
||||
|
@ -2891,11 +2891,10 @@ exports[`13. enderer: crud keepItemSelectionOnPageChange & maxKeepItemSelectionL
|
||||
style="left: 0px;"
|
||||
>
|
||||
<label
|
||||
class="cxd-Checkbox cxd-Checkbox--checkbox"
|
||||
class="cxd-Checkbox cxd-Checkbox--checkbox cxd-Checkbox--full"
|
||||
data-role="checkbox"
|
||||
>
|
||||
<input
|
||||
disabled=""
|
||||
type="checkbox"
|
||||
/>
|
||||
<i />
|
||||
|
@ -2493,6 +2493,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
classnames: cx,
|
||||
keepItemSelectionOnPageChange,
|
||||
maxKeepItemSelectionLength,
|
||||
maxItemSelectionLength,
|
||||
onAction,
|
||||
popOverContainer,
|
||||
translate: __,
|
||||
@ -2580,12 +2581,15 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
: false
|
||||
: multiple,
|
||||
selected:
|
||||
pickerMode || keepItemSelectionOnPageChange
|
||||
pickerMode ||
|
||||
keepItemSelectionOnPageChange ||
|
||||
maxItemSelectionLength
|
||||
? store.selectedItemsAsArray
|
||||
: undefined,
|
||||
strictMode,
|
||||
keepItemSelectionOnPageChange,
|
||||
maxKeepItemSelectionLength,
|
||||
maxItemSelectionLength,
|
||||
valueField: valueField || primaryField,
|
||||
primaryField: primaryField,
|
||||
hideQuickSaveBtn,
|
||||
|
@ -376,6 +376,7 @@ export interface TableProps extends RendererProps, SpinnerExtraProps {
|
||||
selectable?: boolean;
|
||||
selected?: Array<any>;
|
||||
maxKeepItemSelectionLength?: number;
|
||||
maxItemSelectionLength?: number;
|
||||
valueField?: string;
|
||||
draggable?: boolean;
|
||||
columnsTogglable?: boolean | 'auto';
|
||||
@ -508,6 +509,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
'onSelect',
|
||||
'keepItemSelectionOnPageChange',
|
||||
'maxKeepItemSelectionLength',
|
||||
'maxItemSelectionLength',
|
||||
'autoGenerateFilter'
|
||||
];
|
||||
static defaultProps: Partial<TableProps> = {
|
||||
@ -605,6 +607,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
formItem,
|
||||
keepItemSelectionOnPageChange,
|
||||
maxKeepItemSelectionLength,
|
||||
maxItemSelectionLength,
|
||||
onQuery,
|
||||
autoGenerateFilter,
|
||||
loading,
|
||||
@ -641,6 +644,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
combineFromIndex,
|
||||
keepItemSelectionOnPageChange,
|
||||
maxKeepItemSelectionLength,
|
||||
maxItemSelectionLength,
|
||||
loading,
|
||||
canAccessSuperData,
|
||||
lazyRenderAfter,
|
||||
@ -1838,7 +1842,7 @@ export default class Table extends React.Component<TableProps, object> {
|
||||
classPrefix={ns}
|
||||
partial={store.someChecked && !store.allChecked}
|
||||
checked={store.someChecked}
|
||||
disabled={store.isSelectionThresholdReached}
|
||||
disabled={store.isSelectionThresholdReached && !store.someChecked}
|
||||
onChange={this.handleCheckAll}
|
||||
/>
|
||||
) : (
|
||||
|
Loading…
Reference in New Issue
Block a user