diff --git a/docs/zh-CN/components/table.md b/docs/zh-CN/components/table.md index 873be56d8..238fc85c4 100755 --- a/docs/zh-CN/components/table.md +++ b/docs/zh-CN/components/table.md @@ -1872,7 +1872,9 @@ popOver 的其它配置请参考 [popover](./popover) | columnSearch | `searchName: string` 列搜索列名
`searchValue: string` 列搜索数据 | 点击列搜索时触发 | | orderChange | `movedItems: item[]` 已排序数据 | 手动拖拽行排序时触发 | | columnToggled | `columns: item[]` 当前显示的列配置数据 | 点击自定义列时触发 | -| rowClick | `rowItem: object` 行点击数据 | 点击整行时触发 | +| rowClick | `item: object` 行点击数据
`index: number` 行索引 | 点击整行时触发 | +| rowMouseEnter | `item: object` 行移入数据
`index: number` 行索引 | 移入整行时触发 | +| rowMouseLeave | `item: object` 行移出数据
`index: number` 行索引 | 移出整行时触发 | ### 列配置事件表 diff --git a/examples/components/EventAction/cmpt-event-action/TableEvent.jsx b/examples/components/EventAction/cmpt-event-action/TableEvent.jsx index 122fc5707..802277387 100644 --- a/examples/components/EventAction/cmpt-event-action/TableEvent.jsx +++ b/examples/components/EventAction/cmpt-event-action/TableEvent.jsx @@ -256,7 +256,7 @@ export default { actionType: 'toast', args: { msgType: 'info', - msg: '行单击数据:${rowItem|json}' + msg: '行单击数据:${item|json};行索引:${index}' } } ] diff --git a/packages/amis-editor/src/plugin/Table.tsx b/packages/amis-editor/src/plugin/Table.tsx index 5b468b908..7b258e894 100644 --- a/packages/amis-editor/src/plugin/Table.tsx +++ b/packages/amis-editor/src/plugin/Table.tsx @@ -287,7 +287,7 @@ export class TablePlugin extends BasePlugin { { type: 'object', properties: { - 'event.data.rowItem': { + 'event.data.item': { type: 'object', title: '行点击数据' } diff --git a/packages/amis-editor/src/plugin/Table2.tsx b/packages/amis-editor/src/plugin/Table2.tsx index e7ea92a2b..5daf5f9f1 100644 --- a/packages/amis-editor/src/plugin/Table2.tsx +++ b/packages/amis-editor/src/plugin/Table2.tsx @@ -279,7 +279,7 @@ export class Table2Plugin extends BasePlugin { { type: 'object', properties: { - 'event.data.rowItem': { + 'event.data.item': { type: 'object', title: '行点击数据' } diff --git a/packages/amis-ui/src/components/table/index.tsx b/packages/amis-ui/src/components/table/index.tsx index 4eaf95d02..0f46de180 100644 --- a/packages/amis-ui/src/components/table/index.tsx +++ b/packages/amis-ui/src/components/table/index.tsx @@ -1166,13 +1166,20 @@ export class Table extends React.PureComponent { } } - onRowMouseEnter( + async onRowMouseEnter( event: React.ChangeEvent, record?: any, rowIndex?: number ) { 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; while (parent && parent.tagName !== 'TR') { parent = parent.parentElement; @@ -1191,22 +1198,24 @@ export class Table extends React.PureComponent { target = target.closest('tr'); } - this.setState({hoverRow: {target, rowIndex, record}}, () => { - if (onRow) { - onRow.onRowMouseEnter && - onRow.onRowMouseEnter(event, record, rowIndex); - } - }); + this.setState({hoverRow: {target, rowIndex, record}}); } } - onRowMouseLeave( + async onRowMouseLeave( event: React.ChangeEvent, record?: any, rowIndex?: number ) { 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; while (parent && parent.tagName !== 'TR') { parent = parent.parentElement; @@ -1218,12 +1227,6 @@ export class Table extends React.PureComponent { td.classList.remove(cx('Table-cell-row-hover')); } } - - if (record) { - if (onRow) { - onRow.onRowMouseLeave && onRow.onRowMouseLeave(event, record, rowIndex); - } - } } onMouseLeave() { diff --git a/packages/amis/src/renderers/CRUD.tsx b/packages/amis/src/renderers/CRUD.tsx index a44ba2696..2c06c1beb 100644 --- a/packages/amis/src/renderers/CRUD.tsx +++ b/packages/amis/src/renderers/CRUD.tsx @@ -1,6 +1,7 @@ import React from 'react'; import isEqual from 'lodash/isEqual'; import pickBy from 'lodash/pickBy'; +import omitBy from 'lodash/omitBy'; import {Renderer, RendererProps} from 'amis-core'; import {SchemaNode, Schema, ActionObject, PlainObject} from 'amis-core'; import {CRUDStore, ICRUDStore} from 'amis-core'; @@ -44,6 +45,8 @@ import {ActionSchema} from './Action'; import {CardsSchema} from './Cards'; import {ListSchema} from './List'; import {TableSchema} from './Table'; +import type {TableRendererEvent} from './Table'; +import type {CardsRendererEvent} from './Cards'; import {isPureVariable, resolveVariableAndFilter, parseQuery} from 'amis-core'; import type {PaginationProps} from './Pagination'; @@ -74,6 +77,8 @@ export type CRUDToolbarObject = { align?: 'left' | 'right'; }; +export type CRUDRendererEvent = TableRendererEvent | CardsRendererEvent; + export interface CRUDCommonSchema extends BaseSchema, SpinnerExtraProps { /** * 指定为 CRUD 渲染器。 @@ -332,6 +337,19 @@ export interface CRUDProps pickerMode?: boolean; // 选择模式,用做表单中的选择操作 } +const INNER_EVENTS: Array = [ + 'selectedChange', + 'columnSort', + 'columnFilter', + 'columnSearch', + 'columnToggled', + 'orderChange', + 'rowClick', + 'rowMouseEnter', + 'rowMouseLeave', + 'selected' +]; + export default class CRUD extends React.Component { static propsList: Array = [ 'bulkActions', @@ -2230,6 +2248,12 @@ export default class CRUD extends React.Component { 'body', { ...rest, + // 通用事件 例如cus-event 如果直接透传给table 则会被触发2次 + // 因此只将下层组件table、cards中自定义事件透传下去 否则通过crud配置了也不会执行 + onEvent: omitBy( + onEvent, + (event, key: any) => !INNER_EVENTS.includes(key) + ), columns: store.columns ?? rest.columns, type: mode || 'table' }, diff --git a/packages/amis/src/renderers/CRUD2.tsx b/packages/amis/src/renderers/CRUD2.tsx index b76c63a54..f63e78b4e 100644 --- a/packages/amis/src/renderers/CRUD2.tsx +++ b/packages/amis/src/renderers/CRUD2.tsx @@ -1,7 +1,6 @@ import React from 'react'; - +import omitBy from 'lodash/omitBy'; import {Renderer, RendererProps} from 'amis-core'; -import {Action} from '../types'; import {CRUDStore, ICRUDStore} from 'amis-core'; import { createObject, @@ -22,6 +21,7 @@ import {evalExpression, filter} from 'amis-core'; import {isEffectiveApi, isApiOutdated} from 'amis-core'; import findIndex from 'lodash/findIndex'; import {Html, SpinnerExtraProps} from 'amis-ui'; +import {Action} from '../types'; import { BaseSchema, SchemaApi, @@ -33,11 +33,13 @@ import { import {CardsSchema} from './Cards'; import {ListSchema} from './List'; import {TableSchema2} from './Table2'; +import type {Table2RendererEvent} from './Table2'; +import type {CardsRendererEvent} from './Cards'; import {isPureVariable, resolveVariableAndFilter} from 'amis-core'; import {SchemaCollection} from '../Schema'; import upperFirst from 'lodash/upperFirst'; -export type CRUDRendererEvent = 'search'; +export type CRUDRendererEvent = Table2RendererEvent | CardsRendererEvent; export interface CRUD2CommonSchema extends BaseSchema, SpinnerExtraProps { /** @@ -193,6 +195,19 @@ export interface CRUD2Props pickerMode?: boolean; // 选择模式,用做表单中的选择操作 } +const INNER_EVENTS: Array = [ + 'selectedChange', + 'columnSort', + 'columnFilter', + 'columnSearch', + 'columnToggled', + 'orderChange', + 'rowClick', + 'rowMouseEnter', + 'rowMouseLeave', + 'selected' +]; + export default class CRUD2 extends React.Component { static propsList: Array = [ 'mode', @@ -1135,6 +1150,7 @@ export default class CRUD2 extends React.Component { classnames: cx, keepItemSelectionOnPageChange, maxKeepItemSelectionLength, + onEvent, onAction, popOverContainer, translate: __, @@ -1171,6 +1187,12 @@ export default class CRUD2 extends React.Component { 'body', { ...rest, + // 通用事件 例如cus-event 如果直接透传给table 则会被触发2次 + // 因此只将下层组件table、cards中自定义事件透传下去 否则通过crud配置了也不会执行 + onEvent: omitBy( + onEvent, + (event, key: any) => !INNER_EVENTS.includes(key) + ), type: mode, columns: mode.startsWith('table') ? store.columns || columns diff --git a/packages/amis/src/renderers/Cards.tsx b/packages/amis/src/renderers/Cards.tsx index 04517383e..37b563220 100644 --- a/packages/amis/src/renderers/Cards.tsx +++ b/packages/amis/src/renderers/Cards.tsx @@ -1,4 +1,4 @@ -import React, {Fragment} from 'react'; +import React from 'react'; import {findDOMNode} from 'react-dom'; import {Renderer, RendererProps} from 'amis-core'; import {SchemaNode, Schema, ActionObject} from 'amis-core'; @@ -10,7 +10,6 @@ import { getScrollParent, difference, ucFirst, - noop, autobind, createObject } from 'amis-core'; @@ -138,9 +137,9 @@ export interface Column { type: string; [propName: string]: any; } - -export type CardsRendererEvent = 'change'; -export type CardsRendererAction = 'check-all'; +// 如果这里的事件调整,对应CRUD里的事件配置也需要同步修改 +export type CardsRendererEvent = 'selected'; +export type CardsRendererAction = 'toggleSelectAll' | 'selectAll' | 'clearAll'; export interface GridProps extends RendererProps, diff --git a/packages/amis/src/renderers/Table/TableRow.tsx b/packages/amis/src/renderers/Table/TableRow.tsx index c5e87c4ca..cb18dee51 100644 --- a/packages/amis/src/renderers/Table/TableRow.tsx +++ b/packages/amis/src/renderers/Table/TableRow.tsx @@ -1,6 +1,5 @@ import {observer} from 'mobx-react'; import React from 'react'; -import uniq from 'lodash/uniq'; import type {IColumn, IRow} from 'amis-core/lib/store/table'; import {RendererProps} from 'amis-core'; import {Action} from '../Action'; @@ -35,6 +34,31 @@ export class TableRow extends React.Component { this.handleQuickChange = this.handleQuickChange.bind(this); this.handleChange = this.handleChange.bind(this); this.handleItemClick = this.handleItemClick.bind(this); + this.handleMouseEnter = this.handleMouseEnter.bind(this); + this.handleMouseLeave = this.handleMouseLeave.bind(this); + } + + handleMouseEnter(e: React.MouseEvent) { + const {item, itemIndex, data, dispatchEvent} = this.props; + + dispatchEvent( + 'rowMouseEnter', + createObject(data, { + item: item?.data, + index: itemIndex + }) + ); + } + + handleMouseLeave(e: React.MouseEvent) { + const {item, itemIndex, data, dispatchEvent} = this.props; + dispatchEvent( + 'rowMouseLeave', + createObject(data, { + item: item?.data, + index: itemIndex + }) + ); } // 定义点击一行的行为,通过 itemAction配置 @@ -43,13 +67,22 @@ export class TableRow extends React.Component { return; } - const {itemAction, onAction, item, data, dispatchEvent, onCheck} = - this.props; + const { + itemAction, + onAction, + item, + itemIndex, + data, + dispatchEvent, + onCheck + } = this.props; const rendererEvent = await dispatchEvent( 'rowClick', createObject(data, { - rowItem: item?.data + rowItem: item?.data, // 保留rowItem 可能有用户已经在用 兼容之前的版本 + item: item?.data, + index: itemIndex }) ); @@ -144,6 +177,8 @@ export class TableRow extends React.Component { ? this.handleItemClick : undefined } + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} className={cx(itemClassName, { 'is-hovered': item.isHover, 'is-checked': item.checked, @@ -210,6 +245,8 @@ export class TableRow extends React.Component { ? this.handleItemClick : undefined } + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} data-index={item.depth === 1 ? item.newIndex : undefined} data-id={item.id} className={cx( diff --git a/packages/amis/src/renderers/Table/index.tsx b/packages/amis/src/renderers/Table/index.tsx index 8a93895ba..bc23b9f53 100644 --- a/packages/amis/src/renderers/Table/index.tsx +++ b/packages/amis/src/renderers/Table/index.tsx @@ -400,6 +400,7 @@ export type ExportExcelToolbar = SchemaNode & { filename?: string; }; +// 如果这里的事件调整,对应CRUD里的事件配置也需要同步修改 export type TableRendererEvent = | 'selectedChange' | 'columnSort' @@ -407,7 +408,9 @@ export type TableRendererEvent = | 'columnSearch' | 'columnToggled' | 'orderChange' - | 'rowClick'; + | 'rowClick' + | 'rowMouseEnter' + | 'rowMouseLeave'; export type TableRendererAction = | 'selectAll' diff --git a/packages/amis/src/renderers/Table2/index.tsx b/packages/amis/src/renderers/Table2/index.tsx index 8f44ba44e..a14e0557d 100644 --- a/packages/amis/src/renderers/Table2/index.tsx +++ b/packages/amis/src/renderers/Table2/index.tsx @@ -368,13 +368,17 @@ export interface TableSchema2 extends BaseSchema { keepItemSelectionOnPageChange?: boolean; } +// 事件调整 对应CRUD2里的事件配置也需要同步修改 export type Table2RendererEvent = - | 'selected' + | 'selectedChange' | 'columnSort' | 'columnFilter' | 'columnSearch' | 'columnToggled' - | 'dragOver'; + | 'orderChange' + | 'rowClick' + | 'rowMouseEnter' + | 'rowMouseLeave'; export type Table2RendererAction = | 'selectAll' @@ -1137,11 +1141,11 @@ export default class Table2 extends React.Component { const rendererEvent = await dispatchEvent( 'rowClick', - createObject(data, {rowItem}) + createObject(data, {item: rowItem, index: rowIndex}) ); if (rendererEvent?.prevented) { - return rendererEvent?.prevented; + return; } if (rowItem && onRow) { @@ -1149,6 +1153,50 @@ export default class Table2 extends React.Component { } } + @autobind + async handleRowMouseEnter( + event: React.MouseEvent, + 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, + 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 async handleOrderChange( oldIndex: number, @@ -1457,7 +1505,9 @@ export default class Table2 extends React.Component { keyField={keyField} onRow={{ ...onRow, - onRowClick: this.handleRowClick + onRowClick: this.handleRowClick, + onRowMouseEnter: this.handleRowMouseEnter, + onRowMouseLeave: this.handleRowMouseLeave }} > );