diff --git a/packages/amis-core/src/store/crud.ts b/packages/amis-core/src/store/crud.ts index 669b74b8d..c22fca60d 100644 --- a/packages/amis-core/src/store/crud.ts +++ b/packages/amis-core/src/store/crud.ts @@ -183,13 +183,14 @@ export const CRUDStore = ServiceStore.named('CRUDStore') }) ]; }); - items = items.filter((item: any) => arrItems.find(a => a === item)); + items = items.filter((item: any) => + arrItems.find(a => a === item) + ); } - } - else { + } else { items = matchSorter(items, value, { keys: [key] - }) + }); } } } @@ -349,11 +350,11 @@ export const CRUDStore = ServiceStore.named('CRUDStore') }) ]; }); - filteredItems = filteredItems.filter( - item => arrItems.find(a => a === item)); + filteredItems = filteredItems.filter(item => + arrItems.find(a => a === item) + ); } - } - else { + } else { filteredItems = matchSorter(filteredItems, value, { keys: [key] }); diff --git a/packages/amis-core/src/utils/DataScope.ts b/packages/amis-core/src/utils/DataScope.ts index de48a1b93..94f02dcaa 100644 --- a/packages/amis-core/src/utils/DataScope.ts +++ b/packages/amis-core/src/utils/DataScope.ts @@ -97,28 +97,97 @@ export class DataScope { return false; } + assignSchema(target: any, schema: any): any { + // key相同,type也相同 + if (target.type && target.type === schema.type) { + if (target.type === 'array') { + // 先只考虑items,不考虑contains + if (target.items) { + if (Array.isArray(target.items)) { + if (schema.items) { + if (Array.isArray(schema.items)) { + // 如果都是数组,就后者覆盖前者 + return schema.items; + } else { + // 否则,追加 + return { + ...target, + items: [...target.items, schema.items] + }; + } + } else { + return { + ...target, + ...schema + }; + } + } else { + // 非数组,则merge + return { + ...target, + items: this.assignSchema(target.items, schema.items) + }; + } + } else { + return schema; + } + } else if (target.type === 'object' && target.properties) { + let properties: any = {}; + + // 合并属性 + for (let key of Array.from( + new Set([ + ...Object.keys(target.properties), + ...Object.keys(schema.properties) + ]) + )) { + const value = target.properties[key]; + if (value) { + properties[key] = schema.properties[key] + ? this.assignSchema(value, schema.properties[key]) + : value; + } else { + properties[key] = schema.properties[key]; + } + } + return { + ...target, + properties + }; + } else { + return schema; + } + } else { + // key相同、type不同 + if (Array.isArray(target.oneOf)) { + return { + ...target, // 先做个显示过度,因formula还没支持oneOf + oneOf: [...target.oneOf, schema] + }; + } else { + return { + ...target, // 先做个显示过度,因formula还没支持oneOf + oneOf: [target, schema] + }; + } + } + } + getMergedSchema() { const mergedSchema: any = { type: 'object', properties: {} }; - // todo 以后再来细化这一块,先粗略的写个大概 this.schemas.forEach(schema => { const properties: any = schema.properties || {}; Object.keys(properties).forEach(key => { const value = properties[key]; if (mergedSchema.properties[key]) { - if (Array.isArray(mergedSchema.properties[key].oneOf)) { - mergedSchema.properties[key].oneOf.push(); - } else if ( - mergedSchema.properties[key].type && - mergedSchema.properties[key].type !== value.type - ) { - mergedSchema.properties[key] = { - oneOf: [mergedSchema.properties[key], value] - }; - } + mergedSchema.properties[key] = this.assignSchema( + mergedSchema.properties[key], + value + ); } else { mergedSchema.properties[key] = value; } @@ -132,19 +201,14 @@ export class DataScope { options: Array, schema: JSONSchema, path: string = '', - key: string = '', - /** 是否数组元素,数组元素的内容将获取每个成员的对应值 */ - isArrayItem = false, - /** 不是所有的都可以选择,但不影响子元素 */ - disabled?: boolean + key: string = '' ) { // todo 支持 oneOf, anyOf const option: any = { label: schema.title || key, value: path, type: schema.type, - tag: schema.description ?? schema.type, - disabled + tag: schema.description ?? schema.type }; options.push(option); @@ -155,43 +219,32 @@ export class DataScope { keys.forEach(key => { const child: any = schema.properties![key]; - const newPath = isArrayItem ? `ARRAYMAP(${path}, item => item.${key})` : (path + (path ? '.' : '') + key); this.buildOptions( option.children, child, - newPath, - key, - isArrayItem, - false + path + (path ? '.' : '') + key, + key ); }); } else if (schema.type === 'array' && schema.items) { option.children = []; - - this.buildOptions( - option.children, - { - title: '成员', - ...(schema.items as any) - }, - path, - 'items', - true, - true - ); this.buildOptions( option.children, { - title: '总数', - type: 'number' + title: '成员', + ...(schema.items as any), + disabled: true }, - path + (path ? '.' : '') + 'length', - 'length', - true, - isArrayItem + path, + 'items' ); + + option.children = mapTree(option.children, item => ({ + ...item, + disabled: true + })); } } @@ -219,4 +272,8 @@ export class DataScope { } return null; } + + getSchemaById(id: string) { + return this.schemas?.find(item => item.$id === id); + } } diff --git a/packages/amis-editor-core/src/util.ts b/packages/amis-editor-core/src/util.ts index 6013f5096..f7a5e6545 100644 --- a/packages/amis-editor-core/src/util.ts +++ b/packages/amis-editor-core/src/util.ts @@ -931,32 +931,40 @@ export function isObject(curObj: any) { return isObject; } -export function jsonToJsonSchema(json: any = {}) { +export function jsonToJsonSchema( + json: any = {}, + titleBuilder?: (type: string, key: string) => string +) { const jsonschema: any = { type: 'object', properties: {} }; + Object.keys(json).forEach(key => { const value = json[key]; - const type = typeof value; + const type = Array.isArray(value) ? 'array' : typeof value; if (~['string', 'number'].indexOf(type)) { jsonschema.properties[key] = { - type: type, - title: key + type, + title: titleBuilder?.(type, key) || key }; - } else if (type === 'object' && value) { + } else if (~['object', 'array'].indexOf(type) && value) { jsonschema.properties[key] = { - type: 'object', - title: key + type, + title: titleBuilder?.(type, key) || key, + ...(type === 'object' + ? jsonToJsonSchema(value, titleBuilder) + : {items: jsonToJsonSchema(value[0], titleBuilder)}) }; } else { jsonschema.properties[key] = { type: '', - title: key + title: titleBuilder?.(type, key) || key }; } }); + return jsonschema; } diff --git a/packages/amis-editor/src/plugin/CRUD.tsx b/packages/amis-editor/src/plugin/CRUD.tsx index 7bb1d2c4b..ce707d4d1 100644 --- a/packages/amis-editor/src/plugin/CRUD.tsx +++ b/packages/amis-editor/src/plugin/CRUD.tsx @@ -5,6 +5,7 @@ import React from 'react'; import { getI18nEnabled, + jsonToJsonSchema, registerEditorPlugin, tipedLabel } from 'amis-editor-core'; @@ -23,13 +24,14 @@ import { } from 'amis-editor-core'; import {defaultValue, getSchemaTpl} from 'amis-editor-core'; import {isObject, JSONPipeIn} from 'amis-editor-core'; -import {setVariable} from 'amis-core'; +import {setVariable, someTree} from 'amis-core'; import type {ActionSchema} from 'amis'; import type {CRUDCommonSchema} from 'amis'; import {getEnv} from 'mobx-state-tree'; -import {EditorNodeType, RendererPluginAction} from 'amis-editor-core'; +import type {EditorNodeType, RendererPluginAction} from 'amis-editor-core'; import {normalizeApi} from 'amis-core'; import isPlainObject from 'lodash/isPlainObject'; +import omit from 'lodash/omit'; interface ColumnItem { label: string; @@ -1745,7 +1747,92 @@ export class CRUDPlugin extends BasePlugin { return; } - return child.info.plugin.buildDataSchemas(child, undefined, trigger); + let childSchame = await child.info.plugin.buildDataSchemas( + child, + undefined, + trigger + ); + + // 兼容table的rows,并自行merged异步数据 + if (child.type === 'table') { + let cellProperties = {}; + const columns: EditorNodeType = child.children.find( + item => item.isRegion && item.region === 'columns' + ); + + if (trigger) { + const isColumnChild = someTree( + columns?.children, + item => item.id === trigger.id + ); + + // merge异步数据中的单列成员,因为rendererBeforeDispatchEvent无法区分是否需要单列成员 + if (isColumnChild) { + const scope = this.manager.dataSchema.getScope( + `${node.id}-${node.type}` + ); + const menberProps = ( + scope.getSchemaById('crudFetchInitedData')?.properties?.items as any + )?.items?.properties; + + cellProperties = { + ...menberProps, + ...omit( + childSchame.properties, + 'rows', + 'selectedItems', + 'unSelectedItems' + ) + }; + } + } + + childSchame = { + $id: childSchame.$id, + type: childSchame.type, + properties: { + ...cellProperties, + items: childSchame.properties.rows, + selectedItems: childSchame.properties.selectedItems, + unSelectedItems: childSchame.properties.unSelectedItems, + count: { + type: 'number', + title: '总行数' + }, + page: { + type: 'number', + title: '当前页码' + } + } + }; + } + + return childSchame; + } + + rendererBeforeDispatchEvent(node: EditorNodeType, e: any, data: any) { + if (e === 'fetchInited') { + const scope = this.manager.dataSchema.getScope(`${node.id}-${node.type}`); + const jsonschema: any = { + $id: 'crudFetchInitedData', + type: 'object', + ...jsonToJsonSchema( + omit(data, 'selectedItems', 'unSelectedItems'), + (type: string, key: string) => { + if (type === 'array' && key === 'items') { + return '数据列表'; + } + if (type === 'number' && key === 'count') { + return '总行数'; + } + return key; + } + ) + }; + + scope?.removeSchema(jsonschema.$id); + scope?.addSchema(jsonschema); + } } /** crud 不同 mode 之间转换时候,主体的转换 */ diff --git a/packages/amis-editor/src/plugin/Service.tsx b/packages/amis-editor/src/plugin/Service.tsx index f2df6bd31..9b03be719 100644 --- a/packages/amis-editor/src/plugin/Service.tsx +++ b/packages/amis-editor/src/plugin/Service.tsx @@ -68,13 +68,13 @@ export class ServicePlugin extends BasePlugin { }, { eventName: 'fetchInited', - eventLabel: '初始化数据接口请求成功', - description: '远程初始化数据接口请求成功时触发' + eventLabel: '初始化数据接口请求完成', + description: '远程初始化数据接口请求完成时触发' }, { eventName: 'fetchSchemaInited', - eventLabel: '初始化Schema接口请求成功', - description: '远程初始化Schema接口请求成功时触发' + eventLabel: '初始化Schema接口请求完成', + description: '远程初始化Schema接口请求完成时触发' } ]; diff --git a/packages/amis-editor/src/plugin/Table.tsx b/packages/amis-editor/src/plugin/Table.tsx index dbf74c49f..03773eb24 100644 --- a/packages/amis-editor/src/plugin/Table.tsx +++ b/packages/amis-editor/src/plugin/Table.tsx @@ -27,7 +27,11 @@ import { getEventControlConfig, getArgsWrapper } from '../renderer/event-control/helper'; -import {schemaArrayFormat, schemaToArray, resolveArrayDatasource} from '../util'; +import { + schemaArrayFormat, + schemaToArray, + resolveArrayDatasource +} from '../util'; export class TablePlugin extends BasePlugin { // 关联渲染器名字 @@ -289,7 +293,51 @@ export class TablePlugin extends BasePlugin { properties: { 'event.data.item': { type: 'object', - title: '行点击数据' + title: '当前行数据' + }, + 'event.data.index': { + type: 'number', + title: '当前行索引' + } + } + } + ] + }, + { + eventName: 'rowMouseEnter', + eventLabel: '鼠标移入行事件', + description: '移入整行时触发', + dataSchema: [ + { + type: 'object', + properties: { + 'event.data.item': { + type: 'object', + title: '当前行数据' + }, + 'event.data.index': { + type: 'number', + title: '当前行索引' + } + } + } + ] + }, + { + eventName: 'rowMouseLeave', + eventLabel: '鼠标移出行事件', + description: '移出整行时触发', + dataSchema: [ + { + type: 'object', + properties: { + 'event.data.item': { + type: 'object', + title: '当前行数据' + }, + 'event.data.index': { + type: 'number', + title: '当前行索引' } } } @@ -680,16 +728,28 @@ export class TablePlugin extends BasePlugin { item => item.isRegion && item.region === 'columns' ); - for (let current of columns?.children) { - const schema = current.schema; - if (schema.name) { - itemsSchema.properties[schema.name] = current.info?.plugin - ?.buildDataSchemas - ? await current.info.plugin.buildDataSchemas(current, region) - : { - type: 'string', - title: schema.label || schema.name - }; + // todo:以下的处理无效,需要cell实现才能深层细化 + // for (let current of columns?.children) { + // const schema = current.schema; + // if (schema.name) { + // itemsSchema.properties[schema.name] = current.info?.plugin + // ?.buildDataSchemas + // ? await current.info.plugin.buildDataSchemas(current, region) + // : { + // type: 'string', + // title: schema.label || schema.name + // }; + // } + // } + + // 一期先简单处理,上面todo实现之后,这里可以废弃 + // table 无法根据source确定异步数据来源,因此不能在table层做异步数据列的收集 + for (let current of node.schema?.columns) { + if (current.name) { + itemsSchema.properties[current.name] = { + type: 'string', + title: current.label || current.name + }; } } @@ -716,6 +776,16 @@ export class TablePlugin extends BasePlugin { type: 'array', title: '数据列表', items: itemsSchema + }, + selectedItems: { + type: 'array', + title: '已选中行' + // items: itemsSchema + }, + unSelectedItems: { + type: 'array', + title: '未选中行' + // items: itemsSchema } } }; diff --git a/packages/amis/src/renderers/CRUD.tsx b/packages/amis/src/renderers/CRUD.tsx index 848d37e38..b2a396f46 100644 --- a/packages/amis/src/renderers/CRUD.tsx +++ b/packages/amis/src/renderers/CRUD.tsx @@ -775,7 +775,7 @@ export default class CRUD extends React.Component { const ctx = createObject(store.mergedData, { ...selectedItems[0], - currentPageData: store.mergedData.items.concat(), + currentPageData: (store.mergedData?.items || []).concat(), rows: selectedItems, items: selectedItems, selectedItems, @@ -865,7 +865,8 @@ export default class CRUD extends React.Component { }, false, true, - this.props.initFetch !== false + this.props.initFetch !== false, + true ); store.setPristineQuery(); @@ -910,7 +911,8 @@ export default class CRUD extends React.Component { values: Record, jumpToFirstPage: boolean = true, replaceLocation: boolean = false, - search: boolean = true + search: boolean = true, + isInit: boolean = false ) { const { store, @@ -942,8 +944,15 @@ export default class CRUD extends React.Component { perPageField ); this.lastQuery = store.query; + search && - this.search(undefined, undefined, undefined, loadDataOnceFetchOnFilter); + this.search( + undefined, + undefined, + undefined, + loadDataOnceFetchOnFilter, + isInit + ); } handleBulkGo( @@ -1110,7 +1119,8 @@ export default class CRUD extends React.Component { values?: any, silent?: boolean, clearSelection?: boolean, - forceReload = false + forceReload = false, + isInit: boolean = false ) { const { store, @@ -1130,7 +1140,8 @@ export default class CRUD extends React.Component { loadDataOnce, loadDataOnceFetchOnFilter, source, - columns + columns, + dispatchEvent } = this.props; // reload 需要清空用户选择。 @@ -1175,7 +1186,26 @@ export default class CRUD extends React.Component { columns: store.columns ?? columns }) .then(value => { - const {page, lastPage} = store; + const {page, lastPage, data, error, msg} = store; + + if (isInit) { + // 初始化请求完成 + const rendererEvent = dispatchEvent?.( + 'fetchInited', + createObject(this.props.data, { + ...data, + __response: { + error, + msg + } + }) + ); + + if (rendererEvent?.prevented) { + return; + } + } + // 空列表 且 页数已经非法超出,则跳转到最后的合法页数 if ( !store.data.items.length && @@ -1537,8 +1567,8 @@ export default class CRUD extends React.Component { newItems.splice(0, newItems.length - 1) ); } - store.setSelectedItems(newItems); - store.setUnSelectedItems(newUnSelectedItems); + store.updateSelectData(newItems, newUnSelectedItems); + onSelect && onSelect(newItems, newUnSelectedItems); } @@ -1701,7 +1731,6 @@ export default class CRUD extends React.Component { let bulkBtns: Array = []; let itemBtns: Array = []; - const ctx = createObject(store.mergedData, { currentPageData: (store.mergedData?.items || []).concat(), rows: selectedItems.concat(), @@ -2414,4 +2443,13 @@ export class CRUDRenderer extends CRUD { const scoped = this.context as IScopedContext; scoped.close(target); } + + setData(values: object, replace?: boolean) { + return this.props.store.updateData(values, undefined, replace); + } + + getData() { + const {store, data} = this.props; + return store.getData(data); + } } diff --git a/packages/amis/src/renderers/CRUD2.tsx b/packages/amis/src/renderers/CRUD2.tsx index a195762dc..56ca5039c 100644 --- a/packages/amis/src/renderers/CRUD2.tsx +++ b/packages/amis/src/renderers/CRUD2.tsx @@ -910,6 +910,7 @@ export default class CRUD2 extends React.Component { ); } store.updateSelectData(newItems, newUnSelectedItems); + onSelect && onSelect(newItems); } diff --git a/packages/amis/src/renderers/Service.tsx b/packages/amis/src/renderers/Service.tsx index 9ed076076..925cfcd16 100644 --- a/packages/amis/src/renderers/Service.tsx +++ b/packages/amis/src/renderers/Service.tsx @@ -512,10 +512,13 @@ export default class Service extends React.Component { const data = result?.hasOwnProperty('ok') ? result.data ?? {} : result; const {onBulkChange, dispatchEvent, store} = this.props; - dispatchEvent?.('fetchInited', { - ...data, - __response: {msg: store.msg, error: store.error} - }); + dispatchEvent?.( + 'fetchInited', + createObject(this.props.data, { + ...data, + __response: {msg: store.msg, error: store.error} + }) + ); if (!isEmpty(data) && onBulkChange) { onBulkChange(data); diff --git a/packages/amis/src/renderers/Table/TableBody.tsx b/packages/amis/src/renderers/Table/TableBody.tsx index 00a38408a..54ec0c518 100644 --- a/packages/amis/src/renderers/Table/TableBody.tsx +++ b/packages/amis/src/renderers/Table/TableBody.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ClassNamesFn} from 'amis-core'; +import {ClassNamesFn, RendererEvent} from 'amis-core'; import {SchemaNode, ActionObject} from 'amis-core'; import {TableRow} from './TableRow'; @@ -26,6 +26,15 @@ export interface TableBodyProps extends LocaleProps { props: any ) => React.ReactNode; onCheck: (item: IRow, value: boolean, shift?: boolean) => void; + onRowClick: (item: IRow, index: number) => Promise | void>; + onRowMouseEnter: ( + item: IRow, + index: number + ) => Promise | void>; + onRowMouseLeave: ( + item: IRow, + index: number + ) => Promise | void>; onQuickChange?: ( item: IRow, values: object, @@ -69,12 +78,14 @@ export class TableBody extends React.Component { footable, ignoreFootableContent, footableColumns, - itemAction + itemAction, + onRowClick, + onRowMouseEnter, + onRowMouseLeave } = this.props; return rows.map((item: IRow, rowIndex: number) => { const itemProps = buildItemProps ? buildItemProps(item, rowIndex) : null; - const doms = [ { onCheck={onCheck} // todo 先注释 quickEditEnabled={item.depth === 1} onQuickChange={onQuickChange} + onRowClick={onRowClick} + onRowMouseEnter={onRowMouseEnter} + onRowMouseLeave={onRowMouseLeave} {...rowProps} /> ]; @@ -124,6 +138,9 @@ export class TableBody extends React.Component { render={render} onAction={onAction} onCheck={onCheck} + onRowClick={onRowClick} + onRowMouseEnter={onRowMouseEnter} + onRowMouseLeave={onRowMouseLeave} footableMode footableColSpan={columns.length} onQuickChange={onQuickChange} diff --git a/packages/amis/src/renderers/Table/TableContent.tsx b/packages/amis/src/renderers/Table/TableContent.tsx index 1ada5949c..eff2e61c6 100644 --- a/packages/amis/src/renderers/Table/TableContent.tsx +++ b/packages/amis/src/renderers/Table/TableContent.tsx @@ -5,7 +5,8 @@ import { SchemaNode, ActionObject, LocaleProps, - OnEventProps + OnEventProps, + RendererEvent } from 'amis-core'; import {TableBody} from './TableBody'; import {observer} from 'mobx-react'; @@ -42,6 +43,15 @@ export interface TableContentProps extends LocaleProps { props: any ) => React.ReactNode; onCheck: (item: IRow, value: boolean, shift?: boolean) => void; + onRowClick: (item: IRow, index: number) => Promise | void>; + onRowMouseEnter: ( + item: IRow, + index: number + ) => Promise | void>; + onRowMouseLeave: ( + item: IRow, + index: number + ) => Promise | void>; onQuickChange?: ( item: IRow, values: object, @@ -128,6 +138,9 @@ export class TableContent extends React.Component { renderHeadCell, renderCell, onCheck, + onRowClick, + onRowMouseEnter, + onRowMouseLeave, rowClassName, onQuickChange, footable, @@ -233,6 +246,9 @@ export class TableContent extends React.Component { render={render} renderCell={renderCell} onCheck={onCheck} + onRowClick={onRowClick} + onRowMouseEnter={onRowMouseEnter} + onRowMouseLeave={onRowMouseLeave} onQuickChange={onQuickChange} footable={footable} footableColumns={footableColumns} diff --git a/packages/amis/src/renderers/Table/TableRow.tsx b/packages/amis/src/renderers/Table/TableRow.tsx index 3deba426b..4d6af5bb7 100644 --- a/packages/amis/src/renderers/Table/TableRow.tsx +++ b/packages/amis/src/renderers/Table/TableRow.tsx @@ -1,12 +1,21 @@ import {observer} from 'mobx-react'; import React from 'react'; -import type {IColumn, IRow} from 'amis-core'; -import {RendererProps} from 'amis-core'; +import type {IColumn, IRow} from 'amis-core/lib/store/table'; +import {RendererEvent, RendererProps} from 'amis-core'; import {Action} from '../Action'; import {isClickOnInput, createObject} from 'amis-core'; interface TableRowProps extends Pick { onCheck: (item: IRow) => Promise; + onRowClick: (item: IRow, index: number) => Promise | void>; + onRowMouseEnter: ( + item: IRow, + index: number + ) => Promise | void>; + onRowMouseLeave: ( + item: IRow, + index: number + ) => Promise | void>; classPrefix: string; renderCell: ( region: string, @@ -39,26 +48,13 @@ export class TableRow extends React.Component { } handleMouseEnter(e: React.MouseEvent) { - const {item, itemIndex, data, dispatchEvent} = this.props; - - dispatchEvent( - 'rowMouseEnter', - createObject(data, { - item: item?.data, - index: itemIndex - }) - ); + const {item, itemIndex, onRowMouseEnter} = this.props; + onRowMouseEnter?.(item?.data, itemIndex); } handleMouseLeave(e: React.MouseEvent) { - const {item, itemIndex, data, dispatchEvent} = this.props; - dispatchEvent( - 'rowMouseLeave', - createObject(data, { - item: item?.data, - index: itemIndex - }) - ); + const {item, itemIndex, onRowMouseLeave} = this.props; + onRowMouseLeave?.(item?.data, itemIndex); } // 定义点击一行的行为,通过 itemAction配置 @@ -70,24 +66,10 @@ export class TableRow extends React.Component { e.preventDefault(); e.stopPropagation(); - const { - itemAction, - onAction, - item, - itemIndex, - data, - dispatchEvent, - onCheck - } = this.props; + const {itemAction, onAction, item, itemIndex, onCheck, onRowClick} = + this.props; - const rendererEvent = await dispatchEvent( - 'rowClick', - createObject(data, { - rowItem: item?.data, // 保留rowItem 可能有用户已经在用 兼容之前的版本 - item: item?.data, - index: itemIndex - }) - ); + const rendererEvent = await onRowClick?.(item?.data, itemIndex); if (rendererEvent?.prevented) { return; diff --git a/packages/amis/src/renderers/Table/index.tsx b/packages/amis/src/renderers/Table/index.tsx index 6d1ff4318..43a3c5fb1 100644 --- a/packages/amis/src/renderers/Table/index.tsx +++ b/packages/amis/src/renderers/Table/index.tsx @@ -1,7 +1,12 @@ import React from 'react'; import {findDOMNode} from 'react-dom'; import isEqual from 'lodash/isEqual'; -import {ScopedContext, IScopedContext, SchemaExpression} from 'amis-core'; +import { + ScopedContext, + IScopedContext, + SchemaExpression, + extendObject +} from 'amis-core'; import {Renderer, RendererProps} from 'amis-core'; import {SchemaNode, ActionObject, Schema} from 'amis-core'; import forEach from 'lodash/forEach'; @@ -547,6 +552,9 @@ export default class Table extends React.Component { this.handleMouseLeave = this.handleMouseLeave.bind(this); this.subFormRef = this.subFormRef.bind(this); this.handleColumnToggle = this.handleColumnToggle.bind(this); + this.handleRowClick = this.handleRowClick.bind(this); + this.handleRowMouseEnter = this.handleRowMouseEnter.bind(this); + this.handleRowMouseLeave = this.handleRowMouseLeave.bind(this); this.updateAutoFillHeight = this.updateAutoFillHeight.bind(this); @@ -910,16 +918,51 @@ export default class Table extends React.Component { this.syncSelected(); } + handleRowClick(item: IRow, index: number) { + const {dispatchEvent, store, data} = this.props; + return dispatchEvent( + 'rowClick', + createObject(data, { + rowItem: item, // 保留rowItem 可能有用户已经在用 兼容之前的版本 + item, + index + }) + ); + } + + handleRowMouseEnter(item: IRow, index: number) { + const {dispatchEvent, store, data} = this.props; + return dispatchEvent( + 'rowMouseEnter', + createObject(data, { + item, + index + }) + ); + } + + handleRowMouseLeave(item: IRow, index: number) { + const {dispatchEvent, store, data} = this.props; + return dispatchEvent( + 'rowMouseLeave', + createObject(data, { + item, + index + }) + ); + } + async handleCheckAll() { const {store, data, dispatchEvent} = this.props; - - const items = store.getSelectedRows().map(item => item.data); + const items = store.rows.map((row: any) => row.data); + const selectedItems = store.getSelectedRows().map(item => item.data); const rendererEvent = await dispatchEvent( 'selectedChange', createObject(data, { - selectedItems: store.allChecked ? [] : items, - unSelectedItems: store.allChecked ? items : [] + selectedItems: store.allChecked ? [] : selectedItems, + unSelectedItems: store.allChecked ? selectedItems : [], + items }) ); @@ -2236,7 +2279,13 @@ export default class Table extends React.Component { // 操作列不下发loading,否则会导致操作栏里面的所有按钮都出现loading loading: column.type === 'operation' ? false : props.loading, btnDisabled: store.dragging, - data: item.locals, + data: this.props.selectable + ? extendObject(item.locals, { + // 只有table时,也可以获取选中行 + selectedItems: store.selectedRows.map(item => item.data), + unSelectedItems: store.unSelectedRows.map(item => item.data) + }) + : item.locals, value: column.name ? resolveVariable( column.name, @@ -2430,6 +2479,9 @@ export default class Table extends React.Component { render={render} renderCell={this.renderCell} onCheck={this.handleCheck} + onRowClick={this.handleRowClick} + onRowMouseEnter={this.handleRowMouseEnter} + onRowMouseLeave={this.handleRowMouseLeave} onQuickChange={store.dragging ? undefined : this.handleQuickChange} footable={store.footable} ignoreFootableContent @@ -2817,6 +2869,7 @@ export default class Table extends React.Component { { ...this.props, selectedItems: store.selectedRows.map(item => item.data), + unSelectedItems: store.unSelectedRows.map(item => item.data), items: store.rows.map(item => item.data) }, this.renderToolbar @@ -2916,6 +2969,9 @@ export default class Table extends React.Component { renderHeadCell={this.renderHeadCell} renderCell={this.renderCell} onCheck={this.handleCheck} + onRowClick={this.handleRowClick} + onRowMouseEnter={this.handleRowMouseEnter} + onRowMouseLeave={this.handleRowMouseLeave} onQuickChange={store.dragging ? undefined : this.handleQuickChange} footable={store.footable} footableColumns={store.footableColumns} @@ -3091,6 +3147,19 @@ export class TableRenderer extends Table { return scoped.reload(subPath, ctx); } } + + setData(values: any, replace?: boolean) { + const data = { + ...values, + rows: values.rows ?? values.items // 做个兼容 + }; + return this.props.store.updateData(data, undefined, replace); + } + + getData() { + const {store, data} = this.props; + return store.getData(data); + } } export {TableCell};