mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
parent
9e57111669
commit
9bf28b2f85
@ -687,6 +687,54 @@ order: 54
|
||||
}
|
||||
```
|
||||
|
||||
## 树形模式
|
||||
|
||||
配置 `childrenAddable` 为 true,可以开启新增子节点功能。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"data": {
|
||||
"table": [
|
||||
{
|
||||
"a": "a1",
|
||||
"b": "b1"
|
||||
},
|
||||
{
|
||||
"a": "a2",
|
||||
"b": "b2"
|
||||
},
|
||||
{
|
||||
"a": "a3",
|
||||
"b": "b3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"body": [
|
||||
{
|
||||
"type": "input-table",
|
||||
"name": "table",
|
||||
"label": "Table",
|
||||
"addable": true,
|
||||
"childrenAddable": true,
|
||||
"editable": true,
|
||||
"removable": true,
|
||||
"columns": [
|
||||
{
|
||||
"label": "A",
|
||||
"name": "a"
|
||||
},
|
||||
{
|
||||
"label": "B",
|
||||
"name": "b"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 获取父级数据
|
||||
|
||||
默认情况下,Table 内表达项无法获取父级数据域的数据,如下,我们添加 Table 表单项时,尽管 Table 内的文本框的`name`与父级数据域中的`super_text`变量同名,但是没有自动映射值。
|
||||
@ -869,6 +917,7 @@ order: 54
|
||||
| ---------------------------- | ----------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| type | `string` | `"input-table"` | 指定为 Table 渲染器 |
|
||||
| addable | `boolean` | `false` | 是否可增加一行 |
|
||||
| childrenAddable | `boolean` | `false` | 是否可增加子级节点 |
|
||||
| editable | `boolean` | `false` | 是否可编辑 |
|
||||
| removable | `boolean` | `false` | 是否可删除 |
|
||||
| showTableAddBtn | `boolean` | `true` | 是否显示表格操作栏添加按钮,前提是要开启可新增功能 |
|
||||
@ -879,6 +928,8 @@ order: 54
|
||||
| deleteApi | [API](../../../docs/types/api) | - | 删除时提交的 API |
|
||||
| addBtnLabel | `string` | | 增加按钮名称 |
|
||||
| addBtnIcon | `string` | `"plus"` | 增加按钮图标 |
|
||||
| subAddBtnLabel | `string` | | 子级增加按钮名称 |
|
||||
| subAddBtnIcon | `string` | `"sub-plus"` | 子级增加按钮图标 |
|
||||
| copyBtnLabel | `string` | | 复制按钮文字 |
|
||||
| copyBtnIcon | `string` | `"copy"` | 复制按钮图标 |
|
||||
| editBtnLabel | `string` | `""` | 编辑按钮名称 |
|
||||
|
@ -1817,7 +1817,7 @@ popOver 的其它配置请参考 [popover](./popover)
|
||||
|
||||
> 1.5.0 及以上版本
|
||||
|
||||
通过 `autoFillHeight` 可以让表格内容区自适应高度,具体效果请看这个[示例](../../../examples/crud/auto-fill)。
|
||||
通过 `autoFillHeight` 可以让表格内容区自适应高度,具体效果请看这个[示例](../../examples/crud/auto-fill)。
|
||||
|
||||
它的展现效果是整个内容区域高度自适应,表格内容较多时在内容区域内出滚动条,这样顶部筛选和底部翻页的位置都是固定的。
|
||||
|
||||
|
@ -44,7 +44,8 @@ function initChildren(
|
||||
depth: number,
|
||||
pindex: number,
|
||||
parentId: string,
|
||||
path: string = ''
|
||||
path: string = '',
|
||||
getEntryId?: (entry: any, index: number) => string
|
||||
): any {
|
||||
depth += 1;
|
||||
return children.map((item, index) => {
|
||||
@ -53,7 +54,9 @@ function initChildren(
|
||||
: {
|
||||
item
|
||||
};
|
||||
const id = item.__id ?? guid();
|
||||
const id = String(
|
||||
getEntryId ? getEntryId(item, index) : item.__id ?? guid()
|
||||
);
|
||||
|
||||
return {
|
||||
// id: String(item && (item as any)[self.primaryField] || `${pindex}-${depth}-${key}`),
|
||||
@ -72,7 +75,14 @@ function initChildren(
|
||||
rowSpans: {},
|
||||
children:
|
||||
item && Array.isArray(item.children)
|
||||
? initChildren(item.children, depth, index, id, `${path}${index}.`)
|
||||
? initChildren(
|
||||
item.children,
|
||||
depth,
|
||||
index,
|
||||
id,
|
||||
`${path}${index}.`,
|
||||
getEntryId
|
||||
)
|
||||
: []
|
||||
};
|
||||
});
|
||||
@ -387,6 +397,13 @@ export const Row = types
|
||||
);
|
||||
},
|
||||
|
||||
setExpanded(expanded: boolean) {
|
||||
(getParent(self, self.depth * 2) as ITableStore).setExpanded(
|
||||
self as IRow,
|
||||
expanded
|
||||
);
|
||||
},
|
||||
|
||||
change(values: object, savePristine?: boolean) {
|
||||
self.data = immutableExtends(self.data, values);
|
||||
savePristine && (self.pristine = self.data);
|
||||
@ -1450,7 +1467,14 @@ export const TableStore = iRendererStore
|
||||
loading: false,
|
||||
children:
|
||||
item && Array.isArray(item.children)
|
||||
? initChildren(item.children, 1, index, id, `${index}.`)
|
||||
? initChildren(
|
||||
item.children,
|
||||
1,
|
||||
index,
|
||||
id,
|
||||
`${index}.`,
|
||||
getEntryId
|
||||
)
|
||||
: []
|
||||
};
|
||||
});
|
||||
@ -1767,6 +1791,19 @@ export const TableStore = iRendererStore
|
||||
}
|
||||
}
|
||||
|
||||
function setExpanded(row: IRow | string, expanded: boolean) {
|
||||
const id = typeof row === 'string' ? row : row.id;
|
||||
const idx = self.expandedRows.indexOf(id);
|
||||
|
||||
if (expanded) {
|
||||
if (!~idx) {
|
||||
self.expandedRows.push(id);
|
||||
}
|
||||
} else {
|
||||
~idx && self.expandedRows.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function collapseAllAtDepth(depth: number) {
|
||||
let rows = self.getExpandedRows().filter(item => item.depth !== depth);
|
||||
self.expandedRows.replace(rows.map(item => item.id));
|
||||
@ -1928,6 +1965,7 @@ export const TableStore = iRendererStore
|
||||
getToggleShiftRows,
|
||||
toggleExpandAll,
|
||||
toggleExpanded,
|
||||
setExpanded,
|
||||
collapseAllAtDepth,
|
||||
clear,
|
||||
setOrderByInfo,
|
||||
|
@ -643,6 +643,11 @@
|
||||
> .#{$ns}Spinner {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
> [data-role='form-item'] {
|
||||
display: inline-block;
|
||||
min-width: px2rem(160px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import ArrowDoubleLeftIcon from '../icons/arrow-double-left.svg';
|
||||
import ArrowDoubleRightIcon from '../icons/arrow-double-right.svg';
|
||||
import CheckIcon from '../icons/check.svg';
|
||||
import PlusIcon from '../icons/plus.svg';
|
||||
import SubPlusIcon from '../icons/sub-plus.svg';
|
||||
import MinusIcon from '../icons/minus.svg';
|
||||
import PencilIcon from '../icons/pencil.svg';
|
||||
import ViewIcon from '../icons/view.svg';
|
||||
@ -161,6 +162,7 @@ registerIcon('prev', LeftArrowIcon);
|
||||
registerIcon('next', RightArrowIcon);
|
||||
registerIcon('check', CheckIcon);
|
||||
registerIcon('plus', PlusIcon);
|
||||
registerIcon('sub-plus', SubPlusIcon);
|
||||
registerIcon('add', PlusIcon);
|
||||
registerIcon('minus', MinusIcon);
|
||||
registerIcon('pencil', PencilIcon);
|
||||
|
7
packages/amis-ui/src/icons/sub-plus.svg
Normal file
7
packages/amis-ui/src/icons/sub-plus.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="5078" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path
|
||||
d="M128.010044 134.579337h767.979912c47.102768 0 85.355482 38.106432 85.355481 85.28234v206.769449c0 47.175909-38.252714 85.355482-85.355481 85.355481H256.884388v298.561334c0 2.925638 1.023973 5.485571 3.14506 7.533517a10.239732 10.239732 0 0 0 7.533517 3.14506h220.885651a31.962592 31.962592 0 1 1 0 63.998326h-220.885651a71.970689 71.970689 0 0 1-52.807761-21.942283 71.970689 71.970689 0 0 1-21.869142-52.73462V511.986607H128.010044A85.355482 85.355482 0 0 1 42.654563 426.631126V219.861677c0-47.175909 38.252714-85.355482 85.355481-85.355481z m0 313.408945h767.979912a20.479464 20.479464 0 0 0 15.067034-6.216981 20.552605 20.552605 0 0 0 6.290121-15.140175V219.861677c0-5.851276-2.121087-10.971142-6.290121-15.067034a20.479464 20.479464 0 0 0-15.067034-6.290121H128.010044c-5.851276 0-10.971142 2.121087-15.067034 6.290121a20.552605 20.552605 0 0 0-6.290121 15.067034v206.769449c0 5.851276 2.121087 10.971142 6.290121 15.140175a20.552605 20.552605 0 0 0 15.067034 6.216981z m575.69237 532.173508a31.962592 31.962592 0 0 1-31.962592-31.962593v-62.974353h-62.75493a32.035733 32.035733 0 0 1 0-63.998326h62.75493v-62.754929a31.962592 31.962592 0 1 1 63.998325 0v62.754929h62.901212a31.962592 31.962592 0 1 1 0 63.998326h-62.901212v62.901212a31.962592 31.962592 0 0 1-32.035733 32.035734z"
|
||||
p-id="5079"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -253,6 +253,7 @@ register('de-DE', {
|
||||
'Table.valueField': 'valueField muss vorhanden sein',
|
||||
'Table.index': 'Index',
|
||||
'Table.add': 'Neu',
|
||||
'Table.subAddRow': 'Unterzeile hinzufügen',
|
||||
'Table.addButtonDisabledTip':
|
||||
'Reichen Sie bei der Inhaltsbearbeitung zuerst ein und erstellen Sie dann eine neue Option',
|
||||
'Table.toggleColumn': 'Spalten anzeigen',
|
||||
|
@ -227,6 +227,7 @@ register('en-US', {
|
||||
'System.requestError': 'Request error: ',
|
||||
'System.requestErrorStatus': 'Request error, status code: ',
|
||||
'Table.addRow': 'Add row',
|
||||
'Table.subAddRow': 'Add sub row',
|
||||
'Table.copyRow': 'Copy row',
|
||||
'Table.columnsVisibility': 'Click to control columns visibility',
|
||||
'Table.deleteRow': 'Delete current row',
|
||||
|
@ -232,6 +232,7 @@ register('zh-CN', {
|
||||
'System.requestError': '接口报错:',
|
||||
'System.requestErrorStatus': '接口出错,状态码是:',
|
||||
'Table.addRow': '新增一行',
|
||||
'Table.subAddRow': '新增孩子',
|
||||
'Table.copyRow': '复制一行',
|
||||
'Table.columnsVisibility': '点击选择显示列',
|
||||
'Table.deleteRow': '删除当前行',
|
||||
|
@ -26,7 +26,10 @@ import {
|
||||
ListenerAction,
|
||||
evalExpressionWithConditionBuilder,
|
||||
mapTree,
|
||||
isObject
|
||||
isObject,
|
||||
eachTree,
|
||||
everyTree,
|
||||
findTreeIndex
|
||||
} from 'amis-core';
|
||||
import {Button, Icon} from 'amis-ui';
|
||||
import omit from 'lodash/omit';
|
||||
@ -50,6 +53,11 @@ export interface TableControlSchema
|
||||
*/
|
||||
addable?: boolean;
|
||||
|
||||
/**
|
||||
* 是否可以新增子项
|
||||
*/
|
||||
childrenAddable?: boolean;
|
||||
|
||||
/**
|
||||
* 可复制新增
|
||||
*/
|
||||
@ -90,6 +98,16 @@ export interface TableControlSchema
|
||||
*/
|
||||
addBtnIcon?: string;
|
||||
|
||||
/**
|
||||
* 孩子新增按钮文字
|
||||
*/
|
||||
subAddBtnLabel?: string;
|
||||
|
||||
/**
|
||||
* 孩子新增按钮图标
|
||||
*/
|
||||
subAddBtnIcon?: string;
|
||||
|
||||
/**
|
||||
* 可否删除
|
||||
*/
|
||||
@ -231,11 +249,11 @@ export interface TableProps
|
||||
export interface TableState {
|
||||
items: Array<any>;
|
||||
columns: Array<any>;
|
||||
editIndex: number;
|
||||
editIndex: string;
|
||||
isCreateMode?: boolean;
|
||||
page?: number;
|
||||
lastModifiedRow?: {
|
||||
index: number;
|
||||
index: string;
|
||||
data: Record<string, any>;
|
||||
};
|
||||
}
|
||||
@ -260,6 +278,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
placeholder: 'placeholder.empty',
|
||||
scaffold: {},
|
||||
addBtnIcon: 'plus',
|
||||
subAddBtnIcon: 'sub-plus',
|
||||
copyBtnIcon: 'copy',
|
||||
editBtnIcon: 'pencil',
|
||||
deleteBtnIcon: 'minus',
|
||||
@ -306,7 +325,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
|
||||
this.state = {
|
||||
columns: this.buildColumns(props),
|
||||
editIndex: -1,
|
||||
editIndex: '',
|
||||
items: Array.isArray(props.value) ? props.value.concat() : []
|
||||
};
|
||||
|
||||
@ -340,7 +359,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
toUpdate = {
|
||||
...toUpdate,
|
||||
items,
|
||||
editIndex: -1,
|
||||
editIndex: '',
|
||||
columns: this.buildColumns(props)
|
||||
};
|
||||
}
|
||||
@ -356,7 +375,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
toUpdate = {
|
||||
...toUpdate,
|
||||
items: Array.isArray(props.value) ? props.value.concat() : [],
|
||||
editIndex: -1
|
||||
editIndex: ''
|
||||
};
|
||||
}
|
||||
|
||||
@ -409,7 +428,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
const maxLength = this.resolveVariableProps(this.props, 'maxLength');
|
||||
|
||||
// todo: 如果当前正在编辑中,表单提交了,应该先让正在编辑的东西提交然后再做验证。
|
||||
if (~this.state.editIndex) {
|
||||
if (this.state.editIndex) {
|
||||
return __('Table.editing');
|
||||
}
|
||||
|
||||
@ -546,7 +565,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
},
|
||||
() => {
|
||||
if (toAdd.length === 1 && needConfirm !== false) {
|
||||
this.startEdit(items.length - 1, true);
|
||||
this.startEdit(`${items.length - 1}`, true);
|
||||
} else {
|
||||
onChange?.(items);
|
||||
}
|
||||
@ -555,7 +574,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
|
||||
return;
|
||||
} else {
|
||||
return this.addItem(items.length - 1, false);
|
||||
return this.addItem(`${items.length - 1}`, false);
|
||||
}
|
||||
} else if (actionType === 'remove' || actionType === 'delete') {
|
||||
if (!valueField) {
|
||||
@ -564,17 +583,18 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
return env.alert(__('Table.playload'));
|
||||
}
|
||||
|
||||
const items = this.state.items.concat();
|
||||
let items = this.state.items.concat();
|
||||
let toRemove: any = dataMapping(action.payload, ctx);
|
||||
toRemove = Array.isArray(toRemove) ? toRemove : [toRemove];
|
||||
|
||||
toRemove.forEach((toRemove: any) => {
|
||||
const idx = findIndex(
|
||||
const idex = findTreeIndex(
|
||||
items,
|
||||
item => item[valueField as string] == toRemove[valueField as string]
|
||||
);
|
||||
if (~idx) {
|
||||
items.splice(idx, 1);
|
||||
|
||||
if (idex?.length) {
|
||||
items = spliceTree(items, idex, 1);
|
||||
}
|
||||
});
|
||||
|
||||
@ -596,43 +616,56 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
return onAction && onAction(action, ctx, ...rest);
|
||||
}
|
||||
|
||||
async copyItem(index: number) {
|
||||
async copyItem(index: string) {
|
||||
const {needConfirm} = this.props;
|
||||
const items = this.state.items.concat();
|
||||
let items = this.state.items.concat();
|
||||
const indexes = index.split('.').map(item => parseInt(item, 10));
|
||||
const next = indexes.concat();
|
||||
next[next.length - 1] += 1;
|
||||
|
||||
const originItems = items;
|
||||
if (needConfirm === false) {
|
||||
items.splice(index + 1, 0, items[index]);
|
||||
items = spliceTree(items, next, 0, getTree(items, indexes));
|
||||
} else {
|
||||
// 复制相当于新增一行
|
||||
// 需要同addItem一致添加__placeholder属性
|
||||
items.splice(index + 1, 0, {
|
||||
...items[index],
|
||||
items = spliceTree(items, next, 0, {
|
||||
...getTree(items, indexes),
|
||||
__isPlaceholder: true
|
||||
});
|
||||
}
|
||||
index = Math.min(index + 1, items.length - 1);
|
||||
this.reUseRowId(items, originItems, next);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
items
|
||||
},
|
||||
async () => {
|
||||
// 派发add事件
|
||||
const isPrevented = await this.dispatchEvent('add', {index});
|
||||
const isPrevented = await this.dispatchEvent('add', {
|
||||
index: next[next.length - 1],
|
||||
value: getTree(items, next)
|
||||
});
|
||||
if (isPrevented) {
|
||||
return;
|
||||
}
|
||||
if (needConfirm === false) {
|
||||
this.emitValue();
|
||||
} else {
|
||||
this.startEdit(index, true);
|
||||
this.startEdit(next.join('.'), true);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async addItem(index: number, isDispatch: boolean = true) {
|
||||
async addItem(
|
||||
index?: string,
|
||||
isDispatch: boolean = true,
|
||||
callback?: () => void
|
||||
) {
|
||||
index = index || `${this.state.items.length - 1}`;
|
||||
const {needConfirm, scaffold, columns, data} = this.props;
|
||||
const items = this.state.items.concat();
|
||||
let items = this.state.items.concat();
|
||||
let value: any = {
|
||||
__isPlaceholder: true
|
||||
};
|
||||
@ -680,8 +713,13 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
delete value.__isPlaceholder;
|
||||
}
|
||||
|
||||
items.splice(index + 1, 0, value);
|
||||
index = Math.min(index + 1, items.length - 1);
|
||||
const indexes = index.split('.').map(item => parseInt(item, 10));
|
||||
const next = indexes.concat();
|
||||
next[next.length - 1] += 1;
|
||||
|
||||
let originHost = items;
|
||||
items = spliceTree(items, next, 0, value);
|
||||
this.reUseRowId(items, originHost, next);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
@ -695,21 +733,24 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
...((needConfirm === false
|
||||
? {}
|
||||
: {
|
||||
editIndex: index,
|
||||
editIndex: next.join('.'),
|
||||
isCreateMode: true,
|
||||
columns: this.buildColumns(this.props, true, index)
|
||||
columns: this.buildColumns(this.props, true, `${index}`)
|
||||
}) as any)
|
||||
},
|
||||
async () => {
|
||||
if (isDispatch) {
|
||||
const isPrevented = await this.dispatchEvent('add', {index});
|
||||
if (isPrevented) {
|
||||
return;
|
||||
}
|
||||
// todo: add 无法阻止, state 状态也要还原
|
||||
await this.dispatchEvent('add', {
|
||||
index: next[next.length - 1],
|
||||
value
|
||||
});
|
||||
}
|
||||
if (needConfirm === false) {
|
||||
this.emitValue();
|
||||
}
|
||||
|
||||
callback?.();
|
||||
}
|
||||
);
|
||||
|
||||
@ -722,14 +763,24 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async subAddItem(index?: string, isDispatch: boolean = true, item?: any) {
|
||||
return this.addItem(index + '.-1', isDispatch, () => {
|
||||
item?.setExpanded(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击“编辑”按钮
|
||||
* @param index 编辑的行索引
|
||||
*/
|
||||
async editItem(index: number) {
|
||||
async editItem(index: string) {
|
||||
const {items} = this.state;
|
||||
const item = items[index];
|
||||
const isPrevented = await this.dispatchEvent('edit', {index, item});
|
||||
const indexes = index.split('.').map(item => parseInt(item, 10));
|
||||
const item = getTree(items, indexes);
|
||||
const isPrevented = await this.dispatchEvent('edit', {
|
||||
index: indexes[indexes.length - 1],
|
||||
item
|
||||
});
|
||||
!isPrevented && this.startEdit(index, true);
|
||||
}
|
||||
|
||||
@ -753,7 +804,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
return !!rendererEvent?.prevented;
|
||||
}
|
||||
|
||||
startEdit(index: number, isCreate: boolean = false) {
|
||||
startEdit(index: string, isCreate: boolean = false) {
|
||||
this.setState({
|
||||
editIndex: index,
|
||||
isCreateMode: isCreate,
|
||||
@ -777,14 +828,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
);
|
||||
subFormItems.forEach(item => item.props.onFlushChange?.());
|
||||
|
||||
const validateForms: Array<any> = [];
|
||||
Object.keys(this.subForms).forEach(key => {
|
||||
const arr = key.split('-');
|
||||
const num = +arr[1];
|
||||
if (num === this.state.editIndex && this.subForms[key]) {
|
||||
validateForms.push(this.subForms[key]);
|
||||
}
|
||||
});
|
||||
const validateForms: Array<any> = subForms;
|
||||
|
||||
const results = await Promise.all(
|
||||
validateForms
|
||||
@ -797,14 +841,18 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = this.state.items.concat();
|
||||
let items = this.state.items.concat();
|
||||
const indexes = this.state.editIndex
|
||||
.split('.')
|
||||
.map(item => parseInt(item, 10));
|
||||
|
||||
let item = {
|
||||
...items[this.state.editIndex]
|
||||
...getTree(items, indexes)
|
||||
};
|
||||
const isNew = !!item.__isPlaceholder;
|
||||
const confirmEventName = isNew ? 'addConfirm' : 'editConfirm';
|
||||
let isPrevented = await this.dispatchEvent(confirmEventName, {
|
||||
index: this.state.editIndex,
|
||||
index: indexes[indexes.length - 1],
|
||||
item
|
||||
});
|
||||
if (isPrevented) {
|
||||
@ -826,7 +874,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
env.notify('error', apiMsg ?? (remote.msg || __('saveFailed')));
|
||||
const failEventName = isNew ? 'addFail' : 'editFail';
|
||||
this.dispatchEvent(failEventName, {
|
||||
index: this.state.editIndex,
|
||||
index: indexes[indexes.length - 1],
|
||||
item,
|
||||
error: remote
|
||||
});
|
||||
@ -840,11 +888,13 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
|
||||
delete item.__isPlaceholder;
|
||||
items.splice(this.state.editIndex, 1, item);
|
||||
const originItems = items;
|
||||
items = spliceTree(items, indexes, 1, item);
|
||||
this.reUseRowId(items, originItems, indexes);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editIndex: -1,
|
||||
editIndex: '',
|
||||
items: items,
|
||||
columns: this.buildColumns(this.props)
|
||||
},
|
||||
@ -855,7 +905,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
const successEventName = isNew ? 'addSuccess' : 'editSuccess';
|
||||
this.dispatchEvent(successEventName, {
|
||||
index: this.state.editIndex,
|
||||
index: indexes[indexes.length - 1],
|
||||
item
|
||||
});
|
||||
}
|
||||
@ -863,16 +913,20 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
|
||||
cancelEdit() {
|
||||
const items = this.state.items.concat();
|
||||
let items = this.state.items.concat();
|
||||
const lastModifiedRow = this.state.lastModifiedRow;
|
||||
const indexes = this.state.editIndex
|
||||
.split('.')
|
||||
.map(item => parseInt(item, 10));
|
||||
|
||||
let item = {
|
||||
...items[this.state.editIndex]
|
||||
...getTree(items, indexes)
|
||||
};
|
||||
const isNew = !!item.__isPlaceholder;
|
||||
|
||||
const originItems = items;
|
||||
if (isNew) {
|
||||
items.splice(this.state.editIndex, 1);
|
||||
items = spliceTree(items, indexes, 1);
|
||||
} else {
|
||||
/** 恢复编辑前的值 */
|
||||
if (
|
||||
@ -880,16 +934,17 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
~lastModifiedRow?.index &&
|
||||
isObject(lastModifiedRow?.data)
|
||||
) {
|
||||
items.splice(this.state.editIndex, 1, {
|
||||
items = spliceTree(items, indexes, 1, {
|
||||
...item,
|
||||
...lastModifiedRow.data
|
||||
});
|
||||
}
|
||||
}
|
||||
this.reUseRowId(items, originItems, indexes);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
editIndex: -1,
|
||||
editIndex: '',
|
||||
items: items,
|
||||
columns: this.buildColumns(this.props),
|
||||
lastModifiedRow: undefined
|
||||
@ -898,7 +953,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
);
|
||||
}
|
||||
|
||||
async removeItem(index: number) {
|
||||
async removeItem(index: string) {
|
||||
const {
|
||||
value,
|
||||
onChange,
|
||||
@ -910,13 +965,17 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
} = this.props;
|
||||
|
||||
let newValue = Array.isArray(value) ? value.concat() : [];
|
||||
const item = newValue[index];
|
||||
const indexes = index.split('.').map(item => parseInt(item, 10));
|
||||
const item = getTree(newValue, indexes);
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
let isPrevented = await this.dispatchEvent('delete', {index, item});
|
||||
let isPrevented = await this.dispatchEvent('delete', {
|
||||
index: indexes[indexes.length - 1],
|
||||
item
|
||||
});
|
||||
if (isPrevented) {
|
||||
return;
|
||||
}
|
||||
@ -939,15 +998,55 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
'error',
|
||||
(deleteApi as ApiObject)?.messages?.failed ?? __('deleteFailed')
|
||||
);
|
||||
this.dispatchEvent('deleteFail', {index, item, error: result});
|
||||
this.dispatchEvent('deleteFail', {
|
||||
index: indexes[indexes.length - 1],
|
||||
item,
|
||||
error: result
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.removeEntry(item);
|
||||
newValue.splice(index, 1);
|
||||
const originItems = newValue;
|
||||
newValue = spliceTree(newValue, indexes, 1);
|
||||
this.reUseRowId(newValue, originItems, indexes);
|
||||
onChange(newValue);
|
||||
this.dispatchEvent('deleteSuccess', {value: newValue, index, item});
|
||||
this.dispatchEvent('deleteSuccess', {
|
||||
value: newValue,
|
||||
index: indexes[indexes.length - 1],
|
||||
item
|
||||
});
|
||||
}
|
||||
|
||||
rowPathPlusOffset(path: string, offset = 0) {
|
||||
const list = path.split('.').map((item: any) => parseInt(item, 10));
|
||||
list[0] += offset;
|
||||
return list.join('.');
|
||||
}
|
||||
|
||||
reUseRowId(
|
||||
items: Array<any>,
|
||||
originItems: Array<any>,
|
||||
indexes: Array<number>
|
||||
) {
|
||||
// row 不能换 id,否则会重新渲染,导致编辑状态丢失
|
||||
// 展开状态也会丢失
|
||||
let originHost = originItems;
|
||||
let host = items;
|
||||
for (let i = 0, len = indexes.length; i < len; i++) {
|
||||
const idx = indexes[i];
|
||||
if (!originHost?.[idx] || !host?.[idx]) {
|
||||
break;
|
||||
}
|
||||
this.entries.set(
|
||||
host[idx],
|
||||
this.entries.get(originHost[idx]) || this.entityId++
|
||||
);
|
||||
this.entries.delete(originHost[idx]);
|
||||
host = host[idx].children;
|
||||
originHost = originHost[idx].children;
|
||||
}
|
||||
}
|
||||
|
||||
buildItemProps(item: any, index: number) {
|
||||
@ -971,14 +1070,15 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
|
||||
return {
|
||||
quickEditEnabled: this.state.editIndex === index + offset
|
||||
quickEditEnabled:
|
||||
this.state.editIndex === this.rowPathPlusOffset(item.path, offset)
|
||||
};
|
||||
}
|
||||
|
||||
buildColumns(
|
||||
props: TableProps,
|
||||
isCreateMode = false,
|
||||
editRowIndex?: number
|
||||
editRowIndex?: string
|
||||
): Array<any> {
|
||||
const {env, enableStaticTransform, testIdBuilder} = this.props;
|
||||
let columns: Array<any> = Array.isArray(props.columns)
|
||||
@ -999,13 +1099,15 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
offset
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
offset: number;
|
||||
}) =>
|
||||
(~this.state.editIndex && needConfirm !== false) ||
|
||||
(this.state.editIndex && needConfirm !== false) ||
|
||||
maxLength <= this.state.items.length ? null : (
|
||||
<Button
|
||||
classPrefix={ns}
|
||||
@ -1015,7 +1117,12 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
tooltip={__('Table.addRow')}
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.addItem.bind(this, rowIndex + offset, undefined)}
|
||||
onClick={this.addItem.bind(
|
||||
this,
|
||||
this.rowPathPlusOffset(rowIndexPath, offset),
|
||||
undefined,
|
||||
undefined
|
||||
)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`addRow-${rowIndex + offset}`
|
||||
)}
|
||||
@ -1033,18 +1140,69 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
});
|
||||
}
|
||||
|
||||
if (!isStatic && props.childrenAddable && props.showTableAddBtn !== false) {
|
||||
btns.push({
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
offset,
|
||||
row
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
offset: number;
|
||||
row: any;
|
||||
}) =>
|
||||
this.state.editIndex && needConfirm !== false ? null : (
|
||||
<Button
|
||||
classPrefix={ns}
|
||||
size="sm"
|
||||
key={key}
|
||||
level="link"
|
||||
tooltip={__('Table.subAddRow')}
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.subAddItem.bind(
|
||||
this,
|
||||
this.rowPathPlusOffset(rowIndexPath, offset),
|
||||
undefined,
|
||||
row
|
||||
)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`subAddRow-${rowIndex + offset}`
|
||||
)}
|
||||
>
|
||||
{props.subAddBtnIcon ? (
|
||||
<Icon
|
||||
cx={props.classnames}
|
||||
icon={props.subAddBtnIcon}
|
||||
className="icon"
|
||||
/>
|
||||
) : null}
|
||||
{props.subAddBtnLabel ? (
|
||||
<span>{props.subAddBtnLabel}</span>
|
||||
) : null}
|
||||
</Button>
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
if (!isStatic && props.copyable && props.showCopyBtn !== false) {
|
||||
btns.push({
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
offset
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
offset: number;
|
||||
}) =>
|
||||
~this.state.editIndex && needConfirm !== false ? null : (
|
||||
this.state.editIndex && needConfirm !== false ? null : (
|
||||
<Button
|
||||
classPrefix={ns}
|
||||
size="sm"
|
||||
@ -1053,7 +1211,11 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
tooltip={__('Table.copyRow')}
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.copyItem.bind(this, rowIndex + offset, undefined)}
|
||||
onClick={this.copyItem.bind(
|
||||
this,
|
||||
this.rowPathPlusOffset(rowIndexPath, offset),
|
||||
undefined
|
||||
)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`copyRow-${rowIndex + offset}`
|
||||
)}
|
||||
@ -1124,7 +1286,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
* 编辑态仅当前编辑行使用静态展示
|
||||
*/
|
||||
...(enableStaticTransform && props.needConfirm !== false
|
||||
? {staticOn: `${!isCreateMode} || data.index !== ${editRowIndex}`}
|
||||
? {staticOn: `${!isCreateMode} || data.index !== '${editRowIndex}'`}
|
||||
: {})
|
||||
};
|
||||
});
|
||||
@ -1135,15 +1297,17 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
data,
|
||||
offset
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
data: any;
|
||||
offset: number;
|
||||
}) =>
|
||||
~this.state.editIndex || (data && data.__isPlaceholder) ? null : (
|
||||
this.state.editIndex || (data && data.__isPlaceholder) ? null : (
|
||||
<Button
|
||||
classPrefix={ns}
|
||||
size="sm"
|
||||
@ -1154,7 +1318,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
props.popOverContainer || env.getModalContainer
|
||||
}
|
||||
disabled={disabled}
|
||||
onClick={() => this.editItem(rowIndex + offset)}
|
||||
onClick={() =>
|
||||
this.editItem(this.rowPathPlusOffset(rowIndexPath, offset))
|
||||
}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`editRow-${rowIndex + offset}`
|
||||
)}
|
||||
@ -1187,13 +1353,16 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
offset
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
offset: number;
|
||||
}) =>
|
||||
this.state.editIndex === rowIndex + offset ? (
|
||||
this.state.editIndex ===
|
||||
this.rowPathPlusOffset(rowIndexPath, offset) ? (
|
||||
<Button
|
||||
classPrefix={ns}
|
||||
size="sm"
|
||||
@ -1227,13 +1396,16 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
offset
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
offset: number;
|
||||
}) =>
|
||||
this.state.editIndex === rowIndex + offset ? (
|
||||
this.state.editIndex ===
|
||||
this.rowPathPlusOffset(rowIndexPath, offset) ? (
|
||||
<Button
|
||||
classPrefix={ns}
|
||||
size="sm"
|
||||
@ -1282,15 +1454,17 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
children: ({
|
||||
key,
|
||||
rowIndex,
|
||||
rowIndexPath,
|
||||
data,
|
||||
offset
|
||||
}: {
|
||||
key: any;
|
||||
rowIndex: number;
|
||||
rowIndexPath: string;
|
||||
data: any;
|
||||
offset: number;
|
||||
}) =>
|
||||
((~this.state.editIndex || (data && data.__isPlaceholder)) &&
|
||||
((this.state.editIndex || (data && data.__isPlaceholder)) &&
|
||||
needConfirm !== false) ||
|
||||
minLength >= this.state.items.length ? null : (
|
||||
<Button
|
||||
@ -1301,7 +1475,10 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
tooltip={__('Table.deleteRow')}
|
||||
tooltipContainer={props.popOverContainer || env.getModalContainer}
|
||||
disabled={disabled}
|
||||
onClick={this.removeItem.bind(this, rowIndex + offset)}
|
||||
onClick={this.removeItem.bind(
|
||||
this,
|
||||
this.rowPathPlusOffset(rowIndexPath, offset)
|
||||
)}
|
||||
testIdBuilder={testIdBuilder?.getChild(
|
||||
`delRow-${rowIndex + offset}`
|
||||
)}
|
||||
@ -1393,9 +1570,10 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
const editIndex = state.editIndex;
|
||||
const lastModifiedRow = state.lastModifiedRow;
|
||||
|
||||
if (~editIndex) {
|
||||
const items = state.items.concat();
|
||||
const origin = items[editIndex];
|
||||
if (editIndex) {
|
||||
const indexes = editIndex.split('.').map(item => parseInt(item, 10));
|
||||
let items = state.items.concat();
|
||||
const origin = getTree(items, indexes);
|
||||
|
||||
if (!origin) {
|
||||
return newState;
|
||||
@ -1404,9 +1582,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
const value: any = {
|
||||
...rows
|
||||
};
|
||||
this.entries.set(value, this.entries.get(origin) || this.entityId++);
|
||||
this.entries.delete(origin);
|
||||
items.splice(editIndex, 1, value);
|
||||
const originItems = items;
|
||||
items = spliceTree(items, indexes, 1, value);
|
||||
this.reUseRowId(items, originItems, indexes);
|
||||
|
||||
Object.assign(newState, {
|
||||
items,
|
||||
@ -1476,9 +1654,9 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
}
|
||||
);
|
||||
|
||||
const originItems = items;
|
||||
items = spliceTree(items, indexes, 1, data);
|
||||
this.entries.set(data, this.entries.get(origin) || this.entityId++);
|
||||
// this.entries.delete(origin); // 反正最后都会清理的,先不删了吧。
|
||||
this.reUseRowId(items, originItems, indexes);
|
||||
}
|
||||
|
||||
Object.assign(newState, {
|
||||
@ -1552,9 +1730,10 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
...origin,
|
||||
...data
|
||||
};
|
||||
this.entries.set(value, this.entries.get(origin) || this.entityId++);
|
||||
this.entries.delete(origin);
|
||||
|
||||
const originItems = items;
|
||||
items = spliceTree(items, indexes, 1, value);
|
||||
this.reUseRowId(items, originItems, indexes);
|
||||
|
||||
return {
|
||||
items
|
||||
@ -1591,7 +1770,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
|
||||
computedAddBtnDisabled() {
|
||||
const {disabled} = this.props;
|
||||
return disabled || !!~this.state.editIndex;
|
||||
return disabled || !!this.state.editIndex;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -1668,7 +1847,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
value: undefined,
|
||||
saveImmediately: true,
|
||||
disabled,
|
||||
draggable: draggable && !~this.state.editIndex,
|
||||
draggable: draggable && !this.state.editIndex,
|
||||
items: items,
|
||||
getEntryId: this.getEntryId,
|
||||
onSave: this.handleTableSave,
|
||||
@ -1709,7 +1888,7 @@ export default class FormTable extends React.Component<TableProps, TableState> {
|
||||
},
|
||||
{
|
||||
disabled: this.computedAddBtnDisabled(),
|
||||
onClick: () => this.addItem(this.state.items.length),
|
||||
onClick: () => this.addItem(),
|
||||
testIdBuilder: testIdBuilder?.getChild('add')
|
||||
}
|
||||
)
|
||||
@ -1753,25 +1932,31 @@ export class TableControlRenderer extends FormTable {
|
||||
let items = [...this.state.items];
|
||||
const indexs = String(index).split(',');
|
||||
indexs.forEach(i => {
|
||||
const intIndex = Number(i);
|
||||
items.splice(intIndex, 1, value);
|
||||
const indexes = i.split('.').map(item => parseInt(item, 10));
|
||||
items = spliceTree(items, indexes, 1, value);
|
||||
});
|
||||
this.setState({items}, () => {
|
||||
this.emitValue();
|
||||
});
|
||||
} else if (condition !== undefined) {
|
||||
let items = [...this.state.items];
|
||||
for (let i = 0; i < len; i++) {
|
||||
const item = items[i];
|
||||
const isUpdate = await evalExpressionWithConditionBuilder(
|
||||
condition,
|
||||
item
|
||||
);
|
||||
|
||||
if (isUpdate) {
|
||||
items.splice(i, 1, value);
|
||||
}
|
||||
}
|
||||
const promises: Array<() => Promise<any>> = [];
|
||||
everyTree(items, (item, index, paths, indexes) => {
|
||||
promises.unshift(async () => {
|
||||
const isUpdate = await evalExpressionWithConditionBuilder(
|
||||
condition,
|
||||
item
|
||||
);
|
||||
|
||||
if (isUpdate) {
|
||||
items = spliceTree(items, indexes, 1, value);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
await Promise.all(promises.map(fn => fn()));
|
||||
|
||||
this.setState({items}, () => {
|
||||
this.emitValue();
|
||||
@ -1811,7 +1996,7 @@ export class TableControlRenderer extends FormTable {
|
||||
const ctx = this.props.store?.data || {}; // 获取当前上下文数据
|
||||
|
||||
if (actionType === 'addItem') {
|
||||
const items = this.state.items.concat();
|
||||
let items = this.state.items.concat();
|
||||
|
||||
if (addApi || args) {
|
||||
let toAdd: any = null;
|
||||
@ -1842,13 +2027,18 @@ export class TableControlRenderer extends FormTable {
|
||||
)
|
||||
);
|
||||
|
||||
let index = args.index;
|
||||
if (typeof index === 'string' && /^\d+$/.test(index)) {
|
||||
index = parseInt(index, 10);
|
||||
let indexes: Array<number> = [];
|
||||
if (
|
||||
typeof args.index === 'string' &&
|
||||
/^\d+(\.\d+)*$/.test(args.index)
|
||||
) {
|
||||
indexes = (args.index as string).split('.').map(i => parseInt(i, 10));
|
||||
} else if (typeof args.index === 'number') {
|
||||
indexes = [args.index];
|
||||
}
|
||||
|
||||
if (typeof index === 'number') {
|
||||
items.splice(index, 0, ...toAdd);
|
||||
if (indexes.length) {
|
||||
items = spliceTree(items, indexes, 0, ...toAdd);
|
||||
} else {
|
||||
// 没有指定默认插入在最后
|
||||
items.push(...toAdd);
|
||||
@ -1860,7 +2050,9 @@ export class TableControlRenderer extends FormTable {
|
||||
},
|
||||
() => {
|
||||
if (toAdd.length === 1 && needConfirm !== false) {
|
||||
this.startEdit(items.length - 1, true);
|
||||
const next = indexes.concat();
|
||||
next[next.length - 1] += 1;
|
||||
this.startEdit(next.join('.'), true);
|
||||
} else {
|
||||
onChange?.(items);
|
||||
}
|
||||
@ -1868,7 +2060,7 @@ export class TableControlRenderer extends FormTable {
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
return this.addItem(items.length - 1, false);
|
||||
return this.addItem(`${items.length - 1}`, false);
|
||||
}
|
||||
} else if (actionType === 'deleteItem') {
|
||||
const items = [...this.state.items];
|
||||
|
@ -264,6 +264,7 @@ export class TableRow extends React.PureComponent<
|
||||
...rest,
|
||||
width: null,
|
||||
rowIndex: itemIndex,
|
||||
rowIndexPath: item.path,
|
||||
colIndex: column.index,
|
||||
rowPath,
|
||||
key: column.index,
|
||||
@ -327,6 +328,7 @@ export class TableRow extends React.PureComponent<
|
||||
...rest,
|
||||
rowIndex: itemIndex,
|
||||
colIndex: column.index,
|
||||
rowIndexPath: item.path,
|
||||
rowPath,
|
||||
key: column.id,
|
||||
onAction: this.handleAction,
|
||||
|
Loading…
Reference in New Issue
Block a user