feat: crud、crud2支持行移入、行移出事件

This commit is contained in:
wanglinfang 2023-03-31 11:47:22 +08:00
parent 2a79bd18f2
commit 230823daa7
11 changed files with 176 additions and 36 deletions

View File

@ -1872,7 +1872,9 @@ popOver 的其它配置请参考 [popover](./popover)
| columnSearch | `searchName: string` 列搜索列名<br/>`searchValue: string` 列搜索数据 | 点击列搜索时触发 | | columnSearch | `searchName: string` 列搜索列名<br/>`searchValue: string` 列搜索数据 | 点击列搜索时触发 |
| orderChange | `movedItems: item[]` 已排序数据 | 手动拖拽行排序时触发 | | orderChange | `movedItems: item[]` 已排序数据 | 手动拖拽行排序时触发 |
| columnToggled | `columns: item[]` 当前显示的列配置数据 | 点击自定义列时触发 | | columnToggled | `columns: item[]` 当前显示的列配置数据 | 点击自定义列时触发 |
| rowClick | `rowItem: object` 行点击数据 | 点击整行时触发 | | rowClick | `item: object` 行点击数据<br/>`index: number` 行索引 | 点击整行时触发 |
| rowMouseEnter | `item: object` 行移入数据<br/>`index: number` 行索引 | 移入整行时触发 |
| rowMouseLeave | `item: object` 行移出数据<br/>`index: number` 行索引 | 移出整行时触发 |
### 列配置事件表 ### 列配置事件表

View File

@ -256,7 +256,7 @@ export default {
actionType: 'toast', actionType: 'toast',
args: { args: {
msgType: 'info', msgType: 'info',
msg: '行单击数据:${rowItem|json}' msg: '行单击数据:${item|json};行索引:${index}'
} }
} }
] ]

View File

@ -287,7 +287,7 @@ export class TablePlugin extends BasePlugin {
{ {
type: 'object', type: 'object',
properties: { properties: {
'event.data.rowItem': { 'event.data.item': {
type: 'object', type: 'object',
title: '行点击数据' title: '行点击数据'
} }

View File

@ -279,7 +279,7 @@ export class Table2Plugin extends BasePlugin {
{ {
type: 'object', type: 'object',
properties: { properties: {
'event.data.rowItem': { 'event.data.item': {
type: 'object', type: 'object',
title: '行点击数据' title: '行点击数据'
} }

View File

@ -1166,13 +1166,20 @@ export class Table extends React.PureComponent<TableProps, TableState> {
} }
} }
onRowMouseEnter( async onRowMouseEnter(
event: React.ChangeEvent<any>, event: React.ChangeEvent<any>,
record?: any, record?: any,
rowIndex?: number rowIndex?: number
) { ) {
const {classnames: cx, onRow} = this.props; const {classnames: cx, onRow} = this.props;
if (onRow && onRow.onRowMouseEnter) {
const prevented = await onRow.onRowMouseEnter(event, record, rowIndex);
if (prevented) {
return;
}
}
let parent = event.target; let parent = event.target;
while (parent && parent.tagName !== 'TR') { while (parent && parent.tagName !== 'TR') {
parent = parent.parentElement; parent = parent.parentElement;
@ -1191,22 +1198,24 @@ export class Table extends React.PureComponent<TableProps, TableState> {
target = target.closest('tr'); target = target.closest('tr');
} }
this.setState({hoverRow: {target, rowIndex, record}}, () => { this.setState({hoverRow: {target, rowIndex, record}});
if (onRow) {
onRow.onRowMouseEnter &&
onRow.onRowMouseEnter(event, record, rowIndex);
}
});
} }
} }
onRowMouseLeave( async onRowMouseLeave(
event: React.ChangeEvent<any>, event: React.ChangeEvent<any>,
record?: any, record?: any,
rowIndex?: number rowIndex?: number
) { ) {
const {classnames: cx, onRow} = this.props; const {classnames: cx, onRow} = this.props;
if (onRow && onRow.onRowMouseLeave) {
const prevented = await onRow.onRowMouseLeave(event, record, rowIndex);
if (prevented) {
return;
}
}
let parent = event.target; let parent = event.target;
while (parent && parent.tagName !== 'TR') { while (parent && parent.tagName !== 'TR') {
parent = parent.parentElement; parent = parent.parentElement;
@ -1218,12 +1227,6 @@ export class Table extends React.PureComponent<TableProps, TableState> {
td.classList.remove(cx('Table-cell-row-hover')); td.classList.remove(cx('Table-cell-row-hover'));
} }
} }
if (record) {
if (onRow) {
onRow.onRowMouseLeave && onRow.onRowMouseLeave(event, record, rowIndex);
}
}
} }
onMouseLeave() { onMouseLeave() {

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import pickBy from 'lodash/pickBy'; import pickBy from 'lodash/pickBy';
import omitBy from 'lodash/omitBy';
import {Renderer, RendererProps} from 'amis-core'; import {Renderer, RendererProps} from 'amis-core';
import {SchemaNode, Schema, ActionObject, PlainObject} from 'amis-core'; import {SchemaNode, Schema, ActionObject, PlainObject} from 'amis-core';
import {CRUDStore, ICRUDStore} from 'amis-core'; import {CRUDStore, ICRUDStore} from 'amis-core';
@ -44,6 +45,8 @@ import {ActionSchema} from './Action';
import {CardsSchema} from './Cards'; import {CardsSchema} from './Cards';
import {ListSchema} from './List'; import {ListSchema} from './List';
import {TableSchema} from './Table'; import {TableSchema} from './Table';
import type {TableRendererEvent} from './Table';
import type {CardsRendererEvent} from './Cards';
import {isPureVariable, resolveVariableAndFilter, parseQuery} from 'amis-core'; import {isPureVariable, resolveVariableAndFilter, parseQuery} from 'amis-core';
import type {PaginationProps} from './Pagination'; import type {PaginationProps} from './Pagination';
@ -74,6 +77,8 @@ export type CRUDToolbarObject = {
align?: 'left' | 'right'; align?: 'left' | 'right';
}; };
export type CRUDRendererEvent = TableRendererEvent | CardsRendererEvent;
export interface CRUDCommonSchema extends BaseSchema, SpinnerExtraProps { export interface CRUDCommonSchema extends BaseSchema, SpinnerExtraProps {
/** /**
* CRUD * CRUD
@ -332,6 +337,19 @@ export interface CRUDProps
pickerMode?: boolean; // 选择模式,用做表单中的选择操作 pickerMode?: boolean; // 选择模式,用做表单中的选择操作
} }
const INNER_EVENTS: Array<CRUDRendererEvent> = [
'selectedChange',
'columnSort',
'columnFilter',
'columnSearch',
'columnToggled',
'orderChange',
'rowClick',
'rowMouseEnter',
'rowMouseLeave',
'selected'
];
export default class CRUD extends React.Component<CRUDProps, any> { export default class CRUD extends React.Component<CRUDProps, any> {
static propsList: Array<keyof CRUDProps> = [ static propsList: Array<keyof CRUDProps> = [
'bulkActions', 'bulkActions',
@ -2230,6 +2248,12 @@ export default class CRUD extends React.Component<CRUDProps, any> {
'body', 'body',
{ {
...rest, ...rest,
// 通用事件 例如cus-event 如果直接透传给table 则会被触发2次
// 因此只将下层组件table、cards中自定义事件透传下去 否则通过crud配置了也不会执行
onEvent: omitBy(
onEvent,
(event, key: any) => !INNER_EVENTS.includes(key)
),
columns: store.columns ?? rest.columns, columns: store.columns ?? rest.columns,
type: mode || 'table' type: mode || 'table'
}, },

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import omitBy from 'lodash/omitBy';
import {Renderer, RendererProps} from 'amis-core'; import {Renderer, RendererProps} from 'amis-core';
import {Action} from '../types';
import {CRUDStore, ICRUDStore} from 'amis-core'; import {CRUDStore, ICRUDStore} from 'amis-core';
import { import {
createObject, createObject,
@ -22,6 +21,7 @@ import {evalExpression, filter} from 'amis-core';
import {isEffectiveApi, isApiOutdated} from 'amis-core'; import {isEffectiveApi, isApiOutdated} from 'amis-core';
import findIndex from 'lodash/findIndex'; import findIndex from 'lodash/findIndex';
import {Html, SpinnerExtraProps} from 'amis-ui'; import {Html, SpinnerExtraProps} from 'amis-ui';
import {Action} from '../types';
import { import {
BaseSchema, BaseSchema,
SchemaApi, SchemaApi,
@ -33,11 +33,13 @@ import {
import {CardsSchema} from './Cards'; import {CardsSchema} from './Cards';
import {ListSchema} from './List'; import {ListSchema} from './List';
import {TableSchema2} from './Table2'; import {TableSchema2} from './Table2';
import type {Table2RendererEvent} from './Table2';
import type {CardsRendererEvent} from './Cards';
import {isPureVariable, resolveVariableAndFilter} from 'amis-core'; import {isPureVariable, resolveVariableAndFilter} from 'amis-core';
import {SchemaCollection} from '../Schema'; import {SchemaCollection} from '../Schema';
import upperFirst from 'lodash/upperFirst'; import upperFirst from 'lodash/upperFirst';
export type CRUDRendererEvent = 'search'; export type CRUDRendererEvent = Table2RendererEvent | CardsRendererEvent;
export interface CRUD2CommonSchema extends BaseSchema, SpinnerExtraProps { export interface CRUD2CommonSchema extends BaseSchema, SpinnerExtraProps {
/** /**
@ -193,6 +195,19 @@ export interface CRUD2Props
pickerMode?: boolean; // 选择模式,用做表单中的选择操作 pickerMode?: boolean; // 选择模式,用做表单中的选择操作
} }
const INNER_EVENTS: Array<CRUDRendererEvent> = [
'selectedChange',
'columnSort',
'columnFilter',
'columnSearch',
'columnToggled',
'orderChange',
'rowClick',
'rowMouseEnter',
'rowMouseLeave',
'selected'
];
export default class CRUD2 extends React.Component<CRUD2Props, any> { export default class CRUD2 extends React.Component<CRUD2Props, any> {
static propsList: Array<keyof CRUD2Props> = [ static propsList: Array<keyof CRUD2Props> = [
'mode', 'mode',
@ -1135,6 +1150,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
classnames: cx, classnames: cx,
keepItemSelectionOnPageChange, keepItemSelectionOnPageChange,
maxKeepItemSelectionLength, maxKeepItemSelectionLength,
onEvent,
onAction, onAction,
popOverContainer, popOverContainer,
translate: __, translate: __,
@ -1171,6 +1187,12 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
'body', 'body',
{ {
...rest, ...rest,
// 通用事件 例如cus-event 如果直接透传给table 则会被触发2次
// 因此只将下层组件table、cards中自定义事件透传下去 否则通过crud配置了也不会执行
onEvent: omitBy(
onEvent,
(event, key: any) => !INNER_EVENTS.includes(key)
),
type: mode, type: mode,
columns: mode.startsWith('table') columns: mode.startsWith('table')
? store.columns || columns ? store.columns || columns

View File

@ -1,4 +1,4 @@
import React, {Fragment} from 'react'; import React from 'react';
import {findDOMNode} from 'react-dom'; import {findDOMNode} from 'react-dom';
import {Renderer, RendererProps} from 'amis-core'; import {Renderer, RendererProps} from 'amis-core';
import {SchemaNode, Schema, ActionObject} from 'amis-core'; import {SchemaNode, Schema, ActionObject} from 'amis-core';
@ -10,7 +10,6 @@ import {
getScrollParent, getScrollParent,
difference, difference,
ucFirst, ucFirst,
noop,
autobind, autobind,
createObject createObject
} from 'amis-core'; } from 'amis-core';
@ -138,9 +137,9 @@ export interface Column {
type: string; type: string;
[propName: string]: any; [propName: string]: any;
} }
// 如果这里的事件调整对应CRUD里的事件配置也需要同步修改
export type CardsRendererEvent = 'change'; export type CardsRendererEvent = 'selected';
export type CardsRendererAction = 'check-all'; export type CardsRendererAction = 'toggleSelectAll' | 'selectAll' | 'clearAll';
export interface GridProps export interface GridProps
extends RendererProps, extends RendererProps,

View File

@ -1,6 +1,5 @@
import {observer} from 'mobx-react'; import {observer} from 'mobx-react';
import React from 'react'; import React from 'react';
import uniq from 'lodash/uniq';
import type {IColumn, IRow} from 'amis-core/lib/store/table'; import type {IColumn, IRow} from 'amis-core/lib/store/table';
import {RendererProps} from 'amis-core'; import {RendererProps} from 'amis-core';
import {Action} from '../Action'; import {Action} from '../Action';
@ -35,6 +34,31 @@ export class TableRow extends React.Component<TableRowProps> {
this.handleQuickChange = this.handleQuickChange.bind(this); this.handleQuickChange = this.handleQuickChange.bind(this);
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
this.handleItemClick = this.handleItemClick.bind(this); this.handleItemClick = this.handleItemClick.bind(this);
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
}
handleMouseEnter(e: React.MouseEvent<HTMLTableRowElement>) {
const {item, itemIndex, data, dispatchEvent} = this.props;
dispatchEvent(
'rowMouseEnter',
createObject(data, {
item: item?.data,
index: itemIndex
})
);
}
handleMouseLeave(e: React.MouseEvent<HTMLTableRowElement>) {
const {item, itemIndex, data, dispatchEvent} = this.props;
dispatchEvent(
'rowMouseLeave',
createObject(data, {
item: item?.data,
index: itemIndex
})
);
} }
// 定义点击一行的行为,通过 itemAction配置 // 定义点击一行的行为,通过 itemAction配置
@ -43,13 +67,22 @@ export class TableRow extends React.Component<TableRowProps> {
return; return;
} }
const {itemAction, onAction, item, data, dispatchEvent, onCheck} = const {
this.props; itemAction,
onAction,
item,
itemIndex,
data,
dispatchEvent,
onCheck
} = this.props;
const rendererEvent = await dispatchEvent( const rendererEvent = await dispatchEvent(
'rowClick', 'rowClick',
createObject(data, { createObject(data, {
rowItem: item?.data rowItem: item?.data, // 保留rowItem 可能有用户已经在用 兼容之前的版本
item: item?.data,
index: itemIndex
}) })
); );
@ -144,6 +177,8 @@ export class TableRow extends React.Component<TableRowProps> {
? this.handleItemClick ? this.handleItemClick
: undefined : undefined
} }
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
className={cx(itemClassName, { className={cx(itemClassName, {
'is-hovered': item.isHover, 'is-hovered': item.isHover,
'is-checked': item.checked, 'is-checked': item.checked,
@ -210,6 +245,8 @@ export class TableRow extends React.Component<TableRowProps> {
? this.handleItemClick ? this.handleItemClick
: undefined : undefined
} }
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
data-index={item.depth === 1 ? item.newIndex : undefined} data-index={item.depth === 1 ? item.newIndex : undefined}
data-id={item.id} data-id={item.id}
className={cx( className={cx(

View File

@ -400,6 +400,7 @@ export type ExportExcelToolbar = SchemaNode & {
filename?: string; filename?: string;
}; };
// 如果这里的事件调整对应CRUD里的事件配置也需要同步修改
export type TableRendererEvent = export type TableRendererEvent =
| 'selectedChange' | 'selectedChange'
| 'columnSort' | 'columnSort'
@ -407,7 +408,9 @@ export type TableRendererEvent =
| 'columnSearch' | 'columnSearch'
| 'columnToggled' | 'columnToggled'
| 'orderChange' | 'orderChange'
| 'rowClick'; | 'rowClick'
| 'rowMouseEnter'
| 'rowMouseLeave';
export type TableRendererAction = export type TableRendererAction =
| 'selectAll' | 'selectAll'

View File

@ -368,13 +368,17 @@ export interface TableSchema2 extends BaseSchema {
keepItemSelectionOnPageChange?: boolean; keepItemSelectionOnPageChange?: boolean;
} }
// 事件调整 对应CRUD2里的事件配置也需要同步修改
export type Table2RendererEvent = export type Table2RendererEvent =
| 'selected' | 'selectedChange'
| 'columnSort' | 'columnSort'
| 'columnFilter' | 'columnFilter'
| 'columnSearch' | 'columnSearch'
| 'columnToggled' | 'columnToggled'
| 'dragOver'; | 'orderChange'
| 'rowClick'
| 'rowMouseEnter'
| 'rowMouseLeave';
export type Table2RendererAction = export type Table2RendererAction =
| 'selectAll' | 'selectAll'
@ -1137,11 +1141,11 @@ export default class Table2 extends React.Component<Table2Props, object> {
const rendererEvent = await dispatchEvent( const rendererEvent = await dispatchEvent(
'rowClick', 'rowClick',
createObject(data, {rowItem}) createObject(data, {item: rowItem, index: rowIndex})
); );
if (rendererEvent?.prevented) { if (rendererEvent?.prevented) {
return rendererEvent?.prevented; return;
} }
if (rowItem && onRow) { if (rowItem && onRow) {
@ -1149,6 +1153,50 @@ export default class Table2 extends React.Component<Table2Props, object> {
} }
} }
@autobind
async handleRowMouseEnter(
event: React.MouseEvent<HTMLTableRowElement>,
rowItem: any,
rowIndex?: number
) {
const {dispatchEvent, data, onRow} = this.props;
const rendererEvent = await dispatchEvent(
'rowMouseEnter',
createObject(data, {item: rowItem, index: rowIndex})
);
if (rendererEvent?.prevented) {
return;
}
if (rowItem && onRow) {
onRow.onRowMouseEnter && onRow.onRowMouseEnter(event, rowItem, rowIndex);
}
}
@autobind
async handleRowMouseLeave(
event: React.MouseEvent<HTMLTableRowElement>,
rowItem: any,
rowIndex?: number
) {
const {dispatchEvent, data, onRow} = this.props;
const rendererEvent = await dispatchEvent(
'rowMouseLeave',
createObject(data, {item: rowItem, index: rowIndex})
);
if (rendererEvent?.prevented) {
return;
}
if (rowItem && onRow) {
onRow.onRowMouseLeave && onRow.onRowMouseLeave(event, rowItem, rowIndex);
}
}
@autobind @autobind
async handleOrderChange( async handleOrderChange(
oldIndex: number, oldIndex: number,
@ -1457,7 +1505,9 @@ export default class Table2 extends React.Component<Table2Props, object> {
keyField={keyField} keyField={keyField}
onRow={{ onRow={{
...onRow, ...onRow,
onRowClick: this.handleRowClick onRowClick: this.handleRowClick,
onRowMouseEnter: this.handleRowMouseEnter,
onRowMouseLeave: this.handleRowMouseLeave
}} }}
></Table> ></Table>
); );