diff --git a/.prettierrc b/.prettierrc
index 94beb14840..764fbde2e8 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,7 +1,7 @@
{
"singleQuote": true,
"trailingComma": "all",
- "printWidth": 80,
+ "printWidth": 120,
"overrides": [
{
"files": ".prettierrc",
diff --git a/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx b/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx
index 36153c0d51..08463c6fcc 100644
--- a/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx
+++ b/packages/client/src/components/admin-layout/Permissions/ActionPermissionField.tsx
@@ -8,7 +8,8 @@ import { LockOutlined } from '@ant-design/icons';
import cls from 'classnames';
import { uid } from '@formily/shared';
import { Resource } from '../../../resource';
-import { TableRowContext, useTable } from '../../../schemas/table';
+import { TableRowContext } from '../../../schemas/table';
+import { useTable } from '../../../schemas/table';
import { useRequest } from 'ahooks';
import { VisibleContext } from '../../../context';
import { connect, ISchema, observer, useField } from '@formily/react';
diff --git a/packages/client/src/components/admin-layout/Permissions/index.tsx b/packages/client/src/components/admin-layout/Permissions/index.tsx
index 34c9967b15..002fbd6588 100644
--- a/packages/client/src/components/admin-layout/Permissions/index.tsx
+++ b/packages/client/src/components/admin-layout/Permissions/index.tsx
@@ -8,7 +8,8 @@ import { LockOutlined } from '@ant-design/icons';
import cls from 'classnames';
import { uid, isValid } from '@formily/shared';
import { Resource } from '../../../resource';
-import { TableRowContext, useTable } from '../../../schemas/table';
+import { TableRowContext } from '../../../schemas/table';
+import { useTable } from '../../../schemas/table';
import { useRequest } from 'ahooks';
import { VisibleContext } from '../../../context';
import { connect, observer, useForm } from '@formily/react';
diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx
index f7ca5841ae..e9f000609b 100644
--- a/packages/client/src/components/schema-renderer/index.tsx
+++ b/packages/client/src/components/schema-renderer/index.tsx
@@ -52,11 +52,8 @@ import { Menu } from '../../schemas/menu';
import { Password } from '../../schemas/password';
import { Radio } from '../../schemas/radio';
import { Select } from '../../schemas/select';
-import {
- CollectionFieldContext,
- Table,
- TableRowContext,
-} from '../../schemas/table';
+import { Table } from '../../schemas/table';
+import { CollectionFieldContext, TableRowContext } from '../../schemas/table';
import { Tabs } from '../../schemas/tabs';
import { TimePicker } from '../../schemas/time-picker';
import { Upload } from '../../schemas/upload';
diff --git a/packages/client/src/schemas/table/Actions.tsx b/packages/client/src/schemas/table/Actions.tsx
new file mode 100644
index 0000000000..a80a55a61f
--- /dev/null
+++ b/packages/client/src/schemas/table/Actions.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Space } from 'antd';
+import { RecursionField } from '@formily/react';
+import { useDesignable } from '..';
+import { Droppable, SortableItem } from '../../components/Sortable';
+import { getSchemaPath } from '../../components/schema-renderer';
+
+export const Actions = (props: any) => {
+ const { align = 'left' } = props;
+ const { schema, designable } = useDesignable();
+ return (
+
+
+ {schema.mapProperties((s) => {
+ const currentAlign = s['x-align'] || 'left';
+ if (currentAlign !== align) {
+ return null;
+ }
+ return (
+
+
+
+ );
+ })}
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/AddActionButton.tsx b/packages/client/src/schemas/table/AddActionButton.tsx
new file mode 100644
index 0000000000..34780c35b5
--- /dev/null
+++ b/packages/client/src/schemas/table/AddActionButton.tsx
@@ -0,0 +1,76 @@
+import React, { useState } from 'react';
+import { Dropdown, Menu, Button } from 'antd';
+import { SettingOutlined } from '@ant-design/icons';
+import { useTranslation } from 'react-i18next';
+import { Schema } from '@formily/react';
+import { useDesignable, ISchema } from '..';
+import { useDisplayedMapContext, useClient } from '../../constate';
+import SwitchMenuItem from '../../components/SwitchMenuItem';
+import { getSchemaPath } from '../../components/schema-renderer';
+import { generateActionSchema } from './utils';
+
+export const AddActionButton = () => {
+ const { t } = useTranslation();
+ const [visible, setVisible] = useState(false);
+ const displayed = useDisplayedMapContext();
+ const { appendChild, remove } = useDesignable();
+ const { schema, designable } = useDesignable();
+ const { createSchema, removeSchema, updateSchema } = useClient();
+
+ if (!designable || !schema['x-designable-bar']) {
+ return null;
+ }
+ return (
+
+
+ {[
+ { title: t('Filter'), name: 'filter' },
+ { title: t('Export'), name: 'export' },
+ { title: t('Add new'), name: 'create' },
+ { title: t('Delete'), name: 'destroy' },
+ ].map((item) => (
+ {
+ if (!checked) {
+ const s = displayed.get(item.name) as Schema;
+ const path = getSchemaPath(s);
+ displayed.remove(item.name);
+ const removed = remove(path);
+ await removeSchema(removed);
+ } else {
+ const s = generateActionSchema(item.name);
+ const data = appendChild(s);
+ await createSchema(data);
+ }
+ }}
+ />
+ ))}
+
+
+
+ {t('Function')}
+ {t('Popup form')}
+ {t('Flexible popup')}
+
+
+ }
+ >
+ }
+ >
+ {t('Configure actions')}
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/AddColumn.tsx b/packages/client/src/schemas/table/AddColumn.tsx
new file mode 100644
index 0000000000..dcc9825573
--- /dev/null
+++ b/packages/client/src/schemas/table/AddColumn.tsx
@@ -0,0 +1,232 @@
+import React, { useState } from 'react';
+import { Dropdown, Menu, Button } from 'antd';
+import { SettingOutlined } from '@ant-design/icons';
+import { useTranslation } from 'react-i18next';
+import { uid } from '@formily/shared';
+import { FormDialog, FormLayout } from '@formily/antd';
+import { useDesignable, createCollectionField, ISchema } from '..';
+import { useCollectionContext, useCollectionsContext, useDisplayedMapContext, useClient } from '../../constate';
+import { useTable } from './hooks/useTable';
+import SwitchMenuItem from '../../components/SwitchMenuItem';
+import { isAssociation, options } from '../database-field/interfaces';
+import { getSchemaPath, SchemaField } from '../../components/schema-renderer';
+
+export function AddColumn() {
+ const { t } = useTranslation();
+ const [visible, setVisible] = useState(false);
+ const { appendChild, remove } = useDesignable();
+ const { loadCollections } = useCollectionsContext();
+ const { collection, fields, refresh } = useCollectionContext();
+ const displayed = useDisplayedMapContext();
+ const { service } = useTable();
+ const { createSchema, removeSchema, updateSchema } = useClient();
+ return (
+
+
+ {fields.map((field) => (
+ {
+ if (checked) {
+ console.log('SwitchMenuItem.field.name', field.dataType, service.params[0]);
+ const columnSchema: ISchema = {
+ type: 'void',
+ 'x-component': 'Table.Column',
+ 'x-component-props': {
+ fieldName: field.name,
+ },
+ 'x-designable-bar': 'Table.Column.DesignableBar',
+ };
+ if (field.interface === 'linkTo') {
+ columnSchema.properties = {
+ options: {
+ type: 'void',
+ 'x-decorator': 'Form',
+ 'x-component': 'Select.Options.Drawer',
+ 'x-component-props': {
+ useOkAction: '{{ Select.useOkAction }}',
+ },
+ title: "{{t('Select record')}}",
+ properties: {
+ table: {
+ type: 'array',
+ 'x-designable-bar': 'Table.DesignableBar',
+ 'x-decorator': 'BlockItem',
+ 'x-decorator-props': {
+ draggable: false,
+ },
+ 'x-component': 'Table',
+ default: [],
+ 'x-component-props': {
+ rowKey: 'id',
+ useRowSelection: '{{ Select.useRowSelection }}',
+ useSelectedRowKeys: '{{ Select.useSelectedRowKeys }}',
+ onSelect: '{{ Select.useSelect() }}',
+ collectionName: field.target,
+ // dragSort: true,
+ // showIndex: true,
+ refreshRequestOnChange: true,
+ pagination: {
+ pageSize: 10,
+ },
+ },
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Table.ActionBar',
+ 'x-designable-bar': 'Table.ActionBar.DesignableBar',
+ properties: {
+ [uid()]: {
+ type: 'void',
+ title: "{{t('Filter')}}",
+ 'x-decorator': 'AddNew.Displayed',
+ 'x-decorator-props': {
+ displayName: 'filter',
+ },
+ 'x-align': 'left',
+ 'x-component': 'Table.Filter',
+ 'x-designable-bar': 'Table.Filter.DesignableBar',
+ 'x-component-props': {
+ fieldNames: [],
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ option: {
+ type: 'void',
+ 'x-component': 'Select.OptionTag',
+ properties: {
+ [uid()]: {
+ type: 'void',
+ title: "{{t('View record')}}",
+ 'x-component': 'Action.Drawer',
+ 'x-component-props': {
+ bodyStyle: {
+ background: '#f0f2f5',
+ },
+ },
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Tabs',
+ 'x-designable-bar': 'Tabs.DesignableBar',
+ properties: {
+ [uid()]: {
+ type: 'void',
+ title: "{{t('Details')}}",
+ 'x-designable-bar': 'Tabs.TabPane.DesignableBar',
+ 'x-component': 'Tabs.TabPane',
+ 'x-component-props': {},
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-component-props': {
+ addNewComponent: 'AddNew.PaneItem',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+ }
+ const data = appendChild(columnSchema);
+ await createSchema(data);
+ if (isAssociation(field)) {
+ const defaultAppends = service.params[0]?.defaultAppends || [];
+ defaultAppends.push(field.name);
+ await service.run({
+ ...service.params[0],
+ defaultAppends,
+ });
+ }
+ } else {
+ const s: any = displayed.get(field.name);
+ const p = getSchemaPath(s);
+ const removed = remove(p);
+ await removeSchema(removed);
+ displayed.remove(field.name);
+ if (isAssociation(field)) {
+ const defaultAppends = service.params[0]?.defaultAppends || [];
+ const index = defaultAppends.indexOf(field.name);
+ if (index > -1) {
+ defaultAppends.splice(index, 1);
+ }
+ await service.run({
+ ...service.params[0],
+ defaultAppends,
+ });
+ }
+ }
+ // service.refresh();
+ }}
+ />
+ ))}
+
+
+
+ {options.map((option) => (
+
+ {option.children.map((item) => (
+ {
+ setVisible(false);
+ const values = await FormDialog(t('Add field'), () => {
+ return (
+
+
+
+ );
+ }).open({
+ initialValues: {
+ interface: item.name,
+ ...item.default,
+ key: uid(),
+ name: `f_${uid()}`,
+ },
+ });
+ await createCollectionField(collection?.name, values);
+ const data = appendChild({
+ type: 'void',
+ 'x-component': 'Table.Column',
+ 'x-component-props': {
+ fieldName: values.name,
+ },
+ 'x-designable-bar': 'Table.Column.DesignableBar',
+ });
+ await createSchema(data);
+ await refresh();
+ }}
+ >
+ {item.title}
+
+ ))}
+
+ ))}
+
+
+ }
+ >
+ }>
+ {t('Configure fields')}
+
+
+ );
+}
diff --git a/packages/client/src/schemas/table/SimpleDesignableBar.tsx b/packages/client/src/schemas/table/SimpleDesignableBar.tsx
index 058c068158..044d0c6c70 100644
--- a/packages/client/src/schemas/table/SimpleDesignableBar.tsx
+++ b/packages/client/src/schemas/table/SimpleDesignableBar.tsx
@@ -1,24 +1,7 @@
import React, { useContext, useMemo, useRef, useState } from 'react';
import { createForm } from '@formily/core';
-import {
- SchemaOptionsContext,
- Schema,
- useFieldSchema,
- observer,
- SchemaExpressionScopeContext,
- FormProvider,
- ISchema,
- useField,
- useForm,
- RecursionField,
-} from '@formily/react';
-import {
- useSchemaPath,
- SchemaField,
- useDesignable,
- removeSchema,
- updateSchema,
-} from '../';
+import { observer, useField } from '@formily/react';
+import { useSchemaPath, SchemaField, useDesignable, removeSchema, updateSchema } from '../';
import get from 'lodash/get';
import { Button, Dropdown, Menu, Modal, Select, Space } from 'antd';
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
@@ -45,8 +28,7 @@ export const SimpleDesignableBar = observer((props) => {
if (!designable) {
return null;
}
- const defaultPageSize =
- field?.componentProps?.pagination?.defaultPageSize || 10;
+ const defaultPageSize = field?.componentProps?.pagination?.defaultPageSize || 10;
return (
{
{
- const { defaultFilter } = await FormDialog(
- t('Set the data scope'),
- () => {
- return (
-
- {
+ return (
+
+
-
- );
- },
- ).open({
+ },
+ }}
+ />
+
+ );
+ }).open({
initialValues: {
- defaultFilter:
- field?.componentProps?.defaultFilter || {},
+ defaultFilter: field?.componentProps?.defaultFilter || {},
},
});
- schema['x-component-props']['defaultFilter'] =
- defaultFilter;
+ schema['x-component-props']['defaultFilter'] = defaultFilter;
field.componentProps.defaultFilter = defaultFilter;
await updateSchema(schema);
setVisible(false);
diff --git a/packages/client/src/schemas/table/Sortable.tsx b/packages/client/src/schemas/table/Sortable.tsx
index 4728a60d8b..3fc70dfa66 100644
--- a/packages/client/src/schemas/table/Sortable.tsx
+++ b/packages/client/src/schemas/table/Sortable.tsx
@@ -1,42 +1,15 @@
-import React, { forwardRef, useState } from 'react';
-import {
- SortableContext,
- useSortable,
- horizontalListSortingStrategy,
- verticalListSortingStrategy,
- sortableKeyboardCoordinates,
-} from '@dnd-kit/sortable';
+import React, { createContext, useContext, forwardRef, useState } from 'react';
+import { SortableContext, useSortable, horizontalListSortingStrategy } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
-import { Table } from 'antd';
-import { createContext } from 'react';
-import { useContext } from 'react';
-import { range } from 'lodash';
-import parse from 'html-react-parser';
import cls from 'classnames';
+import { DndContext, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
import { MenuOutlined } from '@ant-design/icons';
-
-import {
- DndContext,
- DragOverlay,
- closestCenter,
- KeyboardSensor,
- PointerSensor,
- useSensor,
- useSensors,
- useDroppable,
- useDraggable,
-} from '@dnd-kit/core';
import { createPortal } from 'react-dom';
-import { useRef } from 'react';
import { SortableItem } from '../../components/Sortable';
-import {
- findPropertyByPath,
- getSchemaPath,
- useDesignable,
-} from '../../components/schema-renderer';
+import { findPropertyByPath, getSchemaPath, useDesignable } from '../../components/schema-renderer';
import { updateSchema } from '..';
import { Schema } from '@formily/react';
-import { isColumn, isColumnComponent } from '.';
+import { isColumn } from './utils';
export const RowDraggableContext = createContext({});
export const ColDraggableContext = createContext(null);
@@ -68,9 +41,7 @@ export function SortableColumn(props) {
ref={setDroppableNodeRef}
// {...attributes}
>
-
+
{props.children}
{/*
@@ -81,22 +52,9 @@ export function SortableColumn(props) {
}
export function SortableBodyRow(props: any) {
- const {
- className,
- style: prevStyle,
- ['data-row-key']: dataRowKey,
- ...others
- } = props;
- const {
- isDragging,
- attributes,
- listeners,
- setNodeRef,
- setDraggableNodeRef,
- overIndex,
- transform,
- transition,
- } = useSortable({ id: dataRowKey });
+ const { className, style: prevStyle, ['data-row-key']: dataRowKey, ...others } = props;
+ const { isDragging, attributes, listeners, setNodeRef, setDraggableNodeRef, overIndex, transform, transition } =
+ useSortable({ id: dataRowKey });
const style = {
...prevStyle,
@@ -105,15 +63,9 @@ export function SortableBodyRow(props: any) {
};
return (
-
+
{React.Children.map(props.children, (child, index) => (
-
- {child}
-
+ {child}
))}
@@ -264,8 +214,7 @@ export function SortableBodyCell(props) {
export function SortableRowHandle(props) {
const { className, ...others } = props;
- const { setDraggableNodeRef, attributes, listeners } =
- useContext(RowDraggableContext);
+ const { setDraggableNodeRef, attributes, listeners } = useContext(RowDraggableContext);
return setDraggableNodeRef ? (
{
+ const [visible, setVisible] = useState(false);
+ return (
+
+
+
+
+
+ );
+});
+
+Table.Pagination = TablePagination;
+Table.ActionBar = TableActionBar;
+Table.Filter = TableFilter;
+Table.Filter.DesignableBar = TableFilterDesignableBar;
+Table.ExportActionDesignableBar = TableExportActionDesignableBar;
+Table.Operation = TableOperation;
+Table.Operation.Cell = TableOperationCell;
+Table.Operation.DesignableBar = TableOperationDesignableBar;
+Table.Action = () => null;
+Table.Action.DesignableBar = TableActionDesignableBar;
+Table.Cell = TableCell;
+Table.Column = TableColumn;
+Table.SortHandle = TableSortHandle;
+Table.DesignableBar = TableDesignableBar;
+Table.SimpleDesignableBar = SimpleDesignableBar;
+Table.Index = TableIndex;
+
+Table.useResource = useResource;
+Table.useActionLogDetailsResource = useActionLogDetailsResource;
+Table.useActionLogsResource = useActionLogsResource;
+Table.useTableFilterAction = useTableFilterAction;
+Table.useTableCreateAction = useTableCreateAction;
+Table.useTableUpdateAction = useTableUpdateAction;
+Table.useTableDestroyAction = useTableDestroyAction;
+Table.useTableExportAction = useTableExportAction;
+Table.useTableIndex = useTableIndex;
+Table.useTableRowRecord = useTableRowRecord;
diff --git a/packages/client/src/schemas/table/TableActionBar.tsx b/packages/client/src/schemas/table/TableActionBar.tsx
new file mode 100644
index 0000000000..f46aefe8ad
--- /dev/null
+++ b/packages/client/src/schemas/table/TableActionBar.tsx
@@ -0,0 +1,106 @@
+import React, { useContext, useState } from 'react';
+import { observer } from '@formily/react';
+import cls from 'classnames';
+import { DndContext, DragOverlay } from '@dnd-kit/core';
+import { useDesignable } from '..';
+import { DisplayedMapProvider, useClient } from '../../constate';
+import { AddActionButton } from './AddActionButton';
+import { Actions } from './Actions';
+import { findPropertyByPath } from '../../components/schema-renderer';
+
+export const TableActionBar = observer((props: any) => {
+ const { align = 'top' } = props;
+ // const { schema, designable } = useDesignable();
+ const { root, schema, insertAfter, remove, appendChild } = useDesignable();
+ const moveToAfter = (path1, path2, extra = {}) => {
+ if (!path1 || !path2) {
+ return;
+ }
+ if (path1.join('.') === path2.join('.')) {
+ return;
+ }
+ const data = findPropertyByPath(root, path1);
+ if (!data) {
+ return;
+ }
+ remove(path1);
+ return insertAfter(
+ {
+ ...data.toJSON(),
+ ...extra,
+ },
+ path2,
+ );
+ };
+ const { createSchema, removeSchema, updateSchema } = useClient();
+
+ const [dragOverlayContent, setDragOverlayContent] = useState('');
+ return (
+ {
+ setDragOverlayContent(event.active.data?.current?.title || '');
+ // const previewRef = event.active.data?.current?.previewRef;
+ // if (previewRef) {
+ // setDragOverlayContent(previewRef?.current?.innerHTML);
+ // } else {
+ // setDragOverlayContent('');
+ // }
+ }}
+ onDragEnd={async (event) => {
+ const path1 = event.active?.data?.current?.path;
+ const path2 = event.over?.data?.current?.path;
+ const align = event.over?.data?.current?.align;
+ const draggable = event.over?.data?.current?.draggable;
+ if (!path1 || !path2) {
+ return;
+ }
+ if (path1.join('.') === path2.join('.')) {
+ return;
+ }
+ if (!draggable) {
+ console.log('alignalignalignalign', align);
+ const p = findPropertyByPath(root, path1);
+ if (!p) {
+ return;
+ }
+ remove(path1);
+ const data = appendChild(
+ {
+ ...p.toJSON(),
+ 'x-align': align,
+ },
+ path2,
+ );
+ await updateSchema(data);
+ } else {
+ const data = moveToAfter(path1, path2, {
+ 'x-align': align,
+ });
+ await updateSchema(data);
+ }
+ }}
+ >
+
+ {dragOverlayContent}
+ {/* */}
+
+
+
+
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableActionDesignableBar.tsx b/packages/client/src/schemas/table/TableActionDesignableBar.tsx
new file mode 100644
index 0000000000..ae458f7907
--- /dev/null
+++ b/packages/client/src/schemas/table/TableActionDesignableBar.tsx
@@ -0,0 +1,143 @@
+import React, { useState } from 'react';
+import { Select, Dropdown, Menu, Switch, Space } from 'antd';
+import { MenuOutlined } from '@ant-design/icons';
+import { useField } from '@formily/react';
+import { FormDialog, FormLayout } from '@formily/antd';
+import cls from 'classnames';
+import { Trans, useTranslation } from 'react-i18next';
+import { useDesignable } from '..';
+import { useDisplayedMapContext, useClient } from '../../constate';
+import { DragHandle } from '../../components/Sortable';
+import { SchemaField } from '../../components/schema-renderer';
+
+export const TableActionDesignableBar = () => {
+ const { t } = useTranslation();
+ const { schema, remove, refresh, insertAfter } = useDesignable();
+ const [visible, setVisible] = useState(false);
+ const isPopup = Object.keys(schema.properties || {}).length > 0;
+ const popupSchema = Object.values(schema.properties || {}).shift();
+ const inActionBar = schema.parent['x-component'] === 'Table.ActionBar';
+ const displayed = useDisplayedMapContext();
+ const field = useField();
+ const { createSchema, removeSchema, updateSchema } = useClient();
+ const popupComponent = popupSchema?.['x-component'] || 'Action.Drawer';
+ return (
+
+ {
+ e.stopPropagation();
+ }}
+ className={cls('designable-bar-actions', { active: visible })}
+ >
+
+
+ {
+ setVisible(visible);
+ }}
+ overlay={
+
+ }
+ >
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/TableCell.tsx b/packages/client/src/schemas/table/TableCell.tsx
new file mode 100644
index 0000000000..ed47ab34b1
--- /dev/null
+++ b/packages/client/src/schemas/table/TableCell.tsx
@@ -0,0 +1,54 @@
+import React, { useContext } from 'react';
+import { observer, RecursionField, Schema } from '@formily/react';
+import { cloneDeepWith, set } from 'lodash';
+import { uid, merge } from '@formily/shared';
+import { CollectionFieldContext, TableRowContext } from './context';
+import { Table } from './Table';
+
+export const TableCell = observer((props: any) => {
+ const ctx = useContext(TableRowContext);
+ const schema = props.schema;
+ const collectionField = useContext(CollectionFieldContext);
+ if (schema['x-component'] === 'Table.Operation') {
+ return ;
+ }
+ let uiSchema = collectionField?.uiSchema as Schema;
+ if (uiSchema?.['x-component'] === 'Upload.Attachment') {
+ uiSchema = cloneDeepWith(uiSchema);
+ set(uiSchema, 'x-component-props.size', 'small');
+ }
+ const componentProps = merge(uiSchema?.['x-component-props'] || {}, schema?.['x-component-props'] || {}, {
+ arrayMerge: (t, s) => s,
+ });
+ console.log('Table.Cell', collectionField?.interface, componentProps);
+ return (
+
+
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableColumn.tsx b/packages/client/src/schemas/table/TableColumn.tsx
new file mode 100644
index 0000000000..43287130c3
--- /dev/null
+++ b/packages/client/src/schemas/table/TableColumn.tsx
@@ -0,0 +1,24 @@
+import React, { useContext, useEffect } from 'react';
+import { observer } from '@formily/react';
+import { useDesignable } from '..';
+import { useDisplayedMapContext } from '../../constate';
+import { useCompile } from '../../hooks/useCompile';
+import { CollectionFieldContext } from './context';
+
+export const TableColumn = observer((props: any) => {
+ const collectionField = useContext(CollectionFieldContext);
+ const { schema, DesignableBar } = useDesignable();
+ const compile = useCompile();
+ const displayed = useDisplayedMapContext();
+ useEffect(() => {
+ if (collectionField?.name) {
+ displayed.set(collectionField.name, schema);
+ }
+ }, [collectionField, schema]);
+ return (
+
+ {compile(schema.title || collectionField?.uiSchema?.title)}
+
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableColumnDesignableBar.tsx b/packages/client/src/schemas/table/TableColumnDesignableBar.tsx
new file mode 100644
index 0000000000..097588c724
--- /dev/null
+++ b/packages/client/src/schemas/table/TableColumnDesignableBar.tsx
@@ -0,0 +1,158 @@
+import React, { useContext, useState } from 'react';
+import { useField } from '@formily/react';
+import { Select, Dropdown, Menu, Space } from 'antd';
+import { set } from 'lodash';
+import cls from 'classnames';
+import { MenuOutlined } from '@ant-design/icons';
+import { FormDialog, FormLayout, Submit } from '@formily/antd';
+import { useTranslation } from 'react-i18next';
+import { SchemaField } from '../../components/schema-renderer';
+import { useCollectionsContext, useDisplayedMapContext, useClient } from '../../constate';
+import { DragHandle } from '../../components/Sortable';
+import { useDesignable } from '..';
+import { useCompile } from '../../hooks/useCompile';
+import { useTable } from './hooks/useTable';
+import { CollectionFieldContext } from './context';
+
+export const TableColumnDesignableBar = () => {
+ const field = useField();
+ const { t } = useTranslation();
+ const compile = useCompile();
+ const { service, refresh: refreshTable } = useTable();
+ // const fieldSchema = useFieldSchema();
+ const { schema, remove, refresh, insertAfter } = useDesignable();
+ const [visible, setVisible] = useState(false);
+ const displayed = useDisplayedMapContext();
+ const { getFieldsByCollection } = useCollectionsContext();
+ const collectionField = useContext(CollectionFieldContext);
+ const { createSchema, removeSchema, updateSchema } = useClient();
+ console.log('displayed.map', displayed.map);
+ return (
+
+
{
+ e.stopPropagation();
+ }}
+ className={cls('designable-bar-actions', { active: visible })}
+ >
+
+
+ {
+ setVisible(visible);
+ }}
+ overlay={
+
+ }
+ >
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/TableDesignableBar.tsx b/packages/client/src/schemas/table/TableDesignableBar.tsx
new file mode 100644
index 0000000000..4111c06720
--- /dev/null
+++ b/packages/client/src/schemas/table/TableDesignableBar.tsx
@@ -0,0 +1,275 @@
+import React, { useContext, useState } from 'react';
+import { Modal, Select, Dropdown, Menu, Switch, Space } from 'antd';
+import { MenuOutlined } from '@ant-design/icons';
+import { set } from 'lodash';
+import { observer, useField } from '@formily/react';
+import { FormDialog, FormLayout } from '@formily/antd';
+import cls from 'classnames';
+import { Trans, useTranslation } from 'react-i18next';
+import { useCompile } from '../../hooks/useCompile';
+import { useDesignable } from '..';
+import { DraggableBlockContext } from '../../components/drag-and-drop';
+import { useCollection, useClient } from '../../constate';
+import AddNew from '../add-new';
+import { SchemaField } from '../../components/schema-renderer';
+import { DragHandle } from '../../components/Sortable';
+import { fieldsToSortColumns, fieldsToFilterColumns } from './utils';
+
+export const TableDesignableBar = observer((props) => {
+ const { t } = useTranslation();
+ const compile = useCompile();
+ const field = useField();
+ const { schema, refresh, deepRemove } = useDesignable();
+ const [visible, setVisible] = useState(false);
+ const { dragRef } = useContext(DraggableBlockContext);
+ const defaultPageSize = field?.componentProps?.pagination?.defaultPageSize || 10;
+ const collectionName = field?.componentProps?.collectionName;
+ const { collection, fields } = useCollection({ collectionName });
+ const { createSchema, removeSchema, updateSchema } = useClient();
+ console.log({ collectionName });
+ return (
+
+
{compile(collection?.title || collection?.name)}
+
{
+ e.stopPropagation();
+ }}
+ className={cls('designable-bar-actions', { active: visible })}
+ >
+
+
+
+ {
+ setVisible(visible);
+ }}
+ overlay={
+
+ }
+ >
+
+
+
+
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableExportActionDesignableBar.tsx b/packages/client/src/schemas/table/TableExportActionDesignableBar.tsx
new file mode 100644
index 0000000000..b00603b602
--- /dev/null
+++ b/packages/client/src/schemas/table/TableExportActionDesignableBar.tsx
@@ -0,0 +1,134 @@
+import React, { useState } from 'react';
+import { Dropdown, Menu, Space } from 'antd';
+import { useField } from '@formily/react';
+import { FormDialog, FormLayout } from '@formily/antd';
+import { MenuOutlined } from '@ant-design/icons';
+import cls from 'classnames';
+import { Trans, useTranslation } from 'react-i18next';
+import { DragHandle } from '../../components/Sortable';
+import { useDesignable } from '..';
+import { useCollectionContext, useDisplayedMapContext, useClient } from '../../constate';
+import SwitchMenuItem from '../../components/SwitchMenuItem';
+import { SchemaField } from '../../components/schema-renderer';
+
+export const TableExportActionDesignableBar = () => {
+ const { t } = useTranslation();
+ const { schema, remove, refresh, insertAfter } = useDesignable();
+ const [visible, setVisible] = useState(false);
+ const displayed = useDisplayedMapContext();
+ const { fields } = useCollectionContext();
+ const field = useField();
+ const { createSchema, removeSchema, updateSchema } = useClient();
+ let fieldNames = field.componentProps.fieldNames || [];
+ if (fieldNames.length === 0) {
+ fieldNames = fields.map((field) => field.name);
+ }
+ return (
+
+ {
+ e.stopPropagation();
+ }}
+ className={cls('designable-bar-actions', { active: visible })}
+ >
+
+
+ {
+ setVisible(visible);
+ }}
+ overlay={
+
+ }
+ >
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/TableFilter.tsx b/packages/client/src/schemas/table/TableFilter.tsx
new file mode 100644
index 0000000000..a6f5d7f1f8
--- /dev/null
+++ b/packages/client/src/schemas/table/TableFilter.tsx
@@ -0,0 +1,93 @@
+import React, { useMemo, useState } from 'react';
+import { Popover, Button } from 'antd';
+import { useTranslation } from 'react-i18next';
+import flatten from 'flat';
+import { createForm } from '@formily/core';
+import { FormProvider, observer } from '@formily/react';
+import { FormButtonGroup, Submit } from '@formily/antd';
+import { interfaces } from '../database-field/interfaces';
+import { useDesignable } from '..';
+import { useCollectionContext } from '../../constate';
+import { SchemaField } from '../../components/schema-renderer';
+import { useTable } from './hooks/useTable';
+import { useCompile } from '../../hooks/useCompile';
+import IconPicker from '../../components/icon-picker';
+import { fieldsToFilterColumns } from './utils';
+
+export const TableFilter = observer((props: any) => {
+ const { service } = useTable();
+ const { fieldNames = [] } = props;
+ const compile = useCompile();
+ const { t } = useTranslation();
+ const { schema, DesignableBar } = useDesignable();
+ const form = useMemo(() => createForm(), []);
+ const { fields = [] } = useCollectionContext();
+ const [visible, setVisible] = useState(false);
+ const obj = flatten(form.values.filter || {});
+ console.log('flatten', obj, Object.values(obj));
+ const count = Object.values(obj).filter((i) => (Array.isArray(i) ? i.length : i)).length;
+ const icon = props.icon || 'FilterOutlined';
+ const properties = fieldsToFilterColumns(fields, { fieldNames });
+ schema.mapProperties((p) => {
+ properties[p.name] = p;
+ });
+ return (
+
+
+
+
+
+ {
+ const { filter } = form.values;
+ console.log('Table.Filter', form.values);
+ setVisible(false);
+ return service.run({
+ ...service.params[0],
+ filter,
+ });
+ }}
+ >
+ {t('Submit')}
+
+
+
+
+ }
+ >
+ }>
+ {count > 0 ? t('{{count}} filter items', { count }) : compile(schema.title)}
+
+
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableFilterDesignableBar.tsx b/packages/client/src/schemas/table/TableFilterDesignableBar.tsx
new file mode 100644
index 0000000000..d95b217578
--- /dev/null
+++ b/packages/client/src/schemas/table/TableFilterDesignableBar.tsx
@@ -0,0 +1,140 @@
+import React, { useState } from 'react';
+import { Dropdown, Menu, Space } from 'antd';
+import { useField } from '@formily/react';
+import { FormDialog, FormLayout } from '@formily/antd';
+import { MenuOutlined } from '@ant-design/icons';
+import cls from 'classnames';
+import { useTranslation } from 'react-i18next';
+import { DragHandle } from '../../components/Sortable';
+import { interfaces } from '../database-field/interfaces';
+import { useDesignable } from '..';
+import { useCollectionContext, useDisplayedMapContext, useClient } from '../../constate';
+import SwitchMenuItem from '../../components/SwitchMenuItem';
+import { SchemaField } from '../../components/schema-renderer';
+
+export const TableFilterDesignableBar = () => {
+ const { t } = useTranslation();
+ const { schema, remove, refresh, insertAfter } = useDesignable();
+ const [visible, setVisible] = useState(false);
+ const displayed = useDisplayedMapContext();
+ const { fields } = useCollectionContext();
+ const field = useField();
+ const { createSchema, removeSchema, updateSchema } = useClient();
+ let fieldNames = field.componentProps.fieldNames || [];
+ if (fieldNames.length === 0) {
+ fieldNames = fields.map((field) => field.name);
+ }
+ return (
+
+ {
+ e.stopPropagation();
+ }}
+ className={cls('designable-bar-actions', { active: visible })}
+ >
+
+
+ {
+ setVisible(visible);
+ }}
+ overlay={
+
+ }
+ >
+
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/TableIndex.tsx b/packages/client/src/schemas/table/TableIndex.tsx
new file mode 100644
index 0000000000..f7dbc944ab
--- /dev/null
+++ b/packages/client/src/schemas/table/TableIndex.tsx
@@ -0,0 +1,8 @@
+import React from 'react';
+import { observer } from '@formily/react';
+import { useTableIndex } from './hooks/useTableIndex';
+
+export const TableIndex = observer(() => {
+ const index = useTableIndex();
+ return {index + 1};
+});
diff --git a/packages/client/src/schemas/table/TableMain.tsx b/packages/client/src/schemas/table/TableMain.tsx
new file mode 100644
index 0000000000..38e1eec6c6
--- /dev/null
+++ b/packages/client/src/schemas/table/TableMain.tsx
@@ -0,0 +1,139 @@
+import React, { DndContext } from '@dnd-kit/core';
+import { useState } from 'react';
+import { Table as AntdTable } from 'antd';
+import { findIndex } from 'lodash';
+import cls from 'classnames';
+import { RecursionField, Schema } from '@formily/react';
+import { isValid } from '@formily/shared';
+import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
+import { SortableBodyRow, SortableHeaderCell, SortableHeaderRow } from './Sortable';
+import { TableRowContext } from './context';
+import { useDataSource } from './hooks/useDataSource';
+import { useDefaultRowSelection } from './hooks/useDefaultRowSelection';
+import { useTable } from './hooks/useTable';
+import { useTableActionBars } from './hooks/useTableActionBars';
+import { useTableColumns } from './hooks/useTableColumns';
+import { Table } from './Table';
+
+export const TableMain = () => {
+ const {
+ resource,
+ selectedRowKeys,
+ setSelectedRowKeys,
+ service,
+ field,
+ props: { rowKey, dragSort, showIndex, onSelect, useRowSelection = useDefaultRowSelection },
+ refresh,
+ } = useTable();
+ const columns = useTableColumns();
+ const dataSource = useDataSource();
+ const actionBars = useTableActionBars();
+ const [html, setHtml] = useState('');
+ const { type } = useRowSelection();
+ return (
+
+
{
+ const fromId = event.active?.id as any;
+ const toId = event.over?.id as any;
+ if (isValid(fromId) && isValid(toId)) {
+ const fromIndex = findIndex(field.value, (item) => item[rowKey] === fromId);
+ const toIndex = findIndex(field.value, (item) => item[rowKey] === toId);
+ console.log({ fromId, toId, fromIndex, toIndex });
+ field.move(fromIndex, toIndex);
+ refresh();
+ await resource.sort({
+ resourceKey: fromId,
+ target: {
+ [rowKey]: toId,
+ },
+ });
+ await service.refresh();
+ }
+ }}
+ >
+ {actionBars.map((actionBar) => (
+
+ ))}
+
+ {}}
+ loading={service?.loading}
+ rowKey={rowKey}
+ dataSource={dataSource}
+ columns={columns}
+ // components={{
+ // body: {
+ // row: DragableBodyRow,
+ // },
+ // }}
+ components={{
+ header: {
+ row: SortableHeaderRow,
+ cell: SortableHeaderCell,
+ },
+ body: {
+ // wrapper: (props) => {
+ // return (
+ //
+ //
+ //
+ //
+ // {props.children}
+ //
+ // );
+ // },
+ row: SortableBodyRow,
+ // cell: SortableBodyCell,
+ },
+ }}
+ rowSelection={{
+ type: type || 'checkbox',
+ selectedRowKeys,
+ onChange: (rowKeys, rows) => {
+ setSelectedRowKeys(rowKeys);
+ onSelect && onSelect(rowKeys, rows);
+ },
+ renderCell: (checked, record, _, originNode) => {
+ const index = findIndex(field.value, (item) => item[rowKey] === record[rowKey]);
+ return (
+
+
+ {dragSort &&
}
+ {showIndex &&
}
+ {originNode}
+
+
+ );
+ },
+ }}
+ />
+
+
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/TableOperation.tsx b/packages/client/src/schemas/table/TableOperation.tsx
new file mode 100644
index 0000000000..829fc408e4
--- /dev/null
+++ b/packages/client/src/schemas/table/TableOperation.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { observer } from '@formily/react';
+import { useTranslation } from 'react-i18next';
+import { useDesignable } from '..';
+import { Table } from './Table';
+
+export const TableOperation = observer((props: any) => {
+ const { designable, schema } = useDesignable();
+ const { t } = useTranslation();
+ return (
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableOperationCell.tsx b/packages/client/src/schemas/table/TableOperationCell.tsx
new file mode 100644
index 0000000000..22fced4f03
--- /dev/null
+++ b/packages/client/src/schemas/table/TableOperationCell.tsx
@@ -0,0 +1,13 @@
+import React, { useContext } from 'react';
+import { observer, RecursionField } from '@formily/react';
+import { TableRowContext } from './context';
+
+export const TableOperationCell = observer((props: any) => {
+ const ctx = useContext(TableRowContext);
+ const schema = props.schema;
+ return (
+
+
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableOperationDesignableBar.tsx b/packages/client/src/schemas/table/TableOperationDesignableBar.tsx
new file mode 100644
index 0000000000..5a0c97996d
--- /dev/null
+++ b/packages/client/src/schemas/table/TableOperationDesignableBar.tsx
@@ -0,0 +1,194 @@
+import React, { useState } from 'react';
+import { Dropdown, Menu, Space } from 'antd';
+import { useTranslation } from 'react-i18next';
+import { uid, merge } from '@formily/shared';
+import cls from 'classnames';
+import { MenuOutlined } from '@ant-design/icons';
+import { useDesignable, ISchema } from '..';
+import { getSchemaPath } from '../../components/schema-renderer';
+import { useClient } from '../../constate';
+import SwitchMenuItem from '../../components/SwitchMenuItem';
+import { DragHandle } from '../../components/Sortable';
+
+export const TableOperationDesignableBar = () => {
+ const { t } = useTranslation();
+ const { schema: columnSchema } = useDesignable();
+ const groupSchema = Object.values(columnSchema.properties || {}).shift();
+ const groupPath = getSchemaPath(groupSchema);
+ const { schema, remove, refresh, appendChild } = useDesignable(groupPath);
+ const [visible, setVisible] = useState(false);
+ const { createSchema, removeSchema, updateSchema } = useClient();
+
+ const map = new Map();
+ schema.mapProperties((s) => {
+ if (!s['x-action-type']) {
+ return;
+ }
+ map.set(s['x-action-type'], s.name);
+ });
+ const path = getSchemaPath(schema);
+ return (
+
+ {
+ e.stopPropagation();
+ }}
+ className={cls('designable-bar-actions', { active: visible })}
+ >
+
+
+ {
+ setVisible(visible);
+ }}
+ overlay={
+
+ }
+ >
+
+
+
+
+
+ );
+};
+function generateMenuActionSchema(type) {
+ const actions: { [key: string]: ISchema } = {
+ view: {
+ key: uid(),
+ name: uid(),
+ type: 'void',
+ title: "{{ t('View') }}",
+ 'x-component': 'Action',
+ 'x-component-props': {
+ type: 'link',
+ },
+ 'x-designable-bar': 'Table.Action.DesignableBar',
+ 'x-action-type': 'view',
+ properties: {
+ [uid()]: {
+ type: 'void',
+ title: "{{ t('View record') }}",
+ 'x-component': 'Action.Drawer',
+ 'x-component-props': {
+ bodyStyle: {
+ background: '#f0f2f5',
+ // paddingTop: 0,
+ },
+ },
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Tabs',
+ 'x-designable-bar': 'Tabs.DesignableBar',
+ properties: {
+ [uid()]: {
+ type: 'void',
+ title: "{{ t('Details') }}",
+ 'x-designable-bar': 'Tabs.TabPane.DesignableBar',
+ 'x-component': 'Tabs.TabPane',
+ 'x-component-props': {},
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-component-props': {
+ addNewComponent: 'AddNew.PaneItem',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ update: {
+ key: uid(),
+ name: uid(),
+ type: 'void',
+ title: "{{ t('Edit') }}",
+ 'x-component': 'Action',
+ 'x-component-props': {
+ type: 'link',
+ },
+ 'x-designable-bar': 'Table.Action.DesignableBar',
+ 'x-action-type': 'update',
+ properties: {
+ [uid()]: {
+ type: 'void',
+ title: "{{ t('Edit record') }}",
+ 'x-decorator': 'Form',
+ 'x-decorator-props': {
+ useResource: '{{ Table.useResource }}',
+ useValues: '{{ Table.useTableRowRecord }}',
+ },
+ 'x-component': 'Action.Drawer',
+ 'x-component-props': {
+ useOkAction: '{{ Table.useTableUpdateAction }}',
+ },
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-component-props': {
+ addNewComponent: 'AddNew.FormItem',
+ },
+ },
+ },
+ },
+ },
+ },
+ destroy: {
+ key: uid(),
+ name: uid(),
+ type: 'void',
+ title: "{{ t('Delete') }}",
+ 'x-component': 'Action',
+ 'x-designable-bar': 'Table.Action.DesignableBar',
+ 'x-action-type': 'destroy',
+ 'x-component-props': {
+ useAction: '{{ Table.useTableDestroyAction }}',
+ type: 'link',
+ confirm: {
+ title: "{{ t('Delete record') }}",
+ content: "{{ t('Are you sure you want to delete it?') }}",
+ },
+ },
+ },
+ };
+ return actions[type];
+}
diff --git a/packages/client/src/schemas/table/TablePagination.tsx b/packages/client/src/schemas/table/TablePagination.tsx
new file mode 100644
index 0000000000..f22eada11c
--- /dev/null
+++ b/packages/client/src/schemas/table/TablePagination.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Pagination } from 'antd';
+import { observer } from '@formily/react';
+import { useTable } from './hooks/useTable';
+import { useTotal } from './hooks/useTotal';
+
+export const TablePagination = observer(() => {
+ const { service, pagination, setPagination, props } = useTable();
+ if (!pagination || Object.keys(pagination).length === 0) {
+ return null;
+ }
+ const { clientSidePagination } = props;
+ const total = useTotal();
+ const { page = 1 } = pagination;
+ return (
+
+
{
+ const page = pagination.pageSize !== pageSize ? 1 : current;
+ setPagination({
+ page,
+ pageSize,
+ });
+ // if (clientSidePagination) {
+ // return;
+ // }
+ // service.run({
+ // ...service.params[0],
+ // page,
+ // pageSize,
+ // });
+ }}
+ />
+
+ );
+});
diff --git a/packages/client/src/schemas/table/TableProvider.tsx b/packages/client/src/schemas/table/TableProvider.tsx
new file mode 100644
index 0000000000..7d36edde0f
--- /dev/null
+++ b/packages/client/src/schemas/table/TableProvider.tsx
@@ -0,0 +1,103 @@
+import React, { useState } from 'react';
+import { ArrayField } from '@formily/core';
+import { useField } from '@formily/react';
+import { uid } from '@formily/shared';
+import useRequest from '@ahooksjs/use-request';
+import { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect';
+import { useResource as useGeneralResource } from '../../hooks/useResource';
+import { useDesignable } from '..';
+import { useCollectionContext } from '../../constate';
+import { useDefaultSelectedRowKeys } from './hooks/useDefaultSelectedRowKeys';
+import { usePagination } from './hooks/usePagination';
+import { useCollectionFields } from './hooks/useCollectionFields';
+import { TableContext } from './context';
+import { TableMain } from './TableMain';
+
+export const TableProvider = (props: any) => {
+ const {
+ rowKey = 'id',
+ dataRequest,
+ useResource = useGeneralResource,
+ defaultSelectedRowKeys,
+ useSelectedRowKeys = useDefaultSelectedRowKeys,
+ ...others
+ } = props;
+ const { schema } = useDesignable();
+ const field = useField();
+ const [pagination, setPagination] = usePagination();
+ const { selectedRowKeys, setSelectedRowKeys } = useSelectedRowKeys();
+ console.log('props.useSelectedRowKeys', selectedRowKeys);
+ const [, refresh] = useState(uid());
+ const { resource } = useResource();
+ const { sortableField } = useCollectionContext();
+ const dragSort = props.dragSort;
+ const collectionFields = useCollectionFields(schema);
+ // console.log({ collectionFields, pagination });
+ const getDefaultParams = () => {
+ const defaultParams = { ...pagination };
+ if (dragSort) {
+ defaultParams['sort'] = [sortableField || 'sort'];
+ } else {
+ defaultParams['sort'] = (props.defaultSort || []).join(',');
+ }
+ defaultParams['defaultAppends'] = [...(props.defaultAppends || []), ...collectionFields];
+ if (props.defaultFilter) {
+ defaultParams['defaultFilter'] = props.defaultFilter;
+ }
+ console.log({ defaultParams });
+ return defaultParams;
+ };
+ const service = useRequest(
+ (params?: any) => {
+ if (!resource) {
+ return Promise.resolve({
+ list: field.value,
+ total: field?.value?.length,
+ });
+ }
+ return resource.list(params).then((res) => {
+ return {
+ list: res?.data || [],
+ total: res?.meta?.count || res?.data?.length,
+ };
+ });
+ },
+ {
+ onSuccess(data: any) {
+ field.setValue(data?.list || []);
+ },
+ manual: true,
+ // defaultParams: [getDefaultParams()],
+ },
+ );
+ useDeepCompareEffectNoCheck(() => {
+ service.run(getDefaultParams());
+ }, [pagination?.pageSize, pagination?.page, props.dragSort, props.defaultSort, props.defaultFilter]);
+ return (
+ {
+ const { page = 1, pageSize } = pagination;
+ const total = props.clientSidePagination ? field?.value?.length : service?.data?.total;
+ const maxPage = Math.ceil(total / pageSize);
+ if (page > maxPage) {
+ setPagination({ page: maxPage });
+ } else {
+ refresh(uid());
+ }
+ },
+ selectedRowKeys,
+ setSelectedRowKeys,
+ pagination,
+ setPagination,
+ service,
+ field,
+ schema,
+ props: { ...others, rowKey, dataRequest },
+ }}
+ >
+
+
+ );
+};
diff --git a/packages/client/src/schemas/table/TableSortHandle.tsx b/packages/client/src/schemas/table/TableSortHandle.tsx
new file mode 100644
index 0000000000..226874d13a
--- /dev/null
+++ b/packages/client/src/schemas/table/TableSortHandle.tsx
@@ -0,0 +1,7 @@
+import React from 'react';
+import { observer } from '@formily/react';
+import { SortableRowHandle } from './Sortable';
+
+export const TableSortHandle = observer((props: any) => {
+ return ;
+});
diff --git a/packages/client/src/schemas/table/context.ts b/packages/client/src/schemas/table/context.ts
new file mode 100644
index 0000000000..d09324c5ff
--- /dev/null
+++ b/packages/client/src/schemas/table/context.ts
@@ -0,0 +1,6 @@
+import { createContext } from 'react';
+import { ITableContext, ITableRowContext } from './types';
+
+export const TableContext = createContext({} as any);
+export const CollectionFieldContext = createContext(null);
+export const TableRowContext = createContext(null);
diff --git a/packages/client/src/schemas/table/demos/demo3.tsx b/packages/client/src/schemas/table/demos/demo3.tsx
index 50a639eb45..10f7622f32 100644
--- a/packages/client/src/schemas/table/demos/demo3.tsx
+++ b/packages/client/src/schemas/table/demos/demo3.tsx
@@ -3,10 +3,7 @@ import { range } from 'lodash';
import { ISchema } from '@formily/react';
import { SchemaRenderer } from '../..';
import { uid } from '@formily/shared';
-import {
- CollectionsProvider,
- DesignableSwitchProvider,
-} from '@nocobase/client/src/constate';
+import { CollectionsProvider, DesignableSwitchProvider } from '@nocobase/client/src/constate';
const schema = {
name: 'table1',
diff --git a/packages/client/src/schemas/table/hooks/useActionLogDetailsResource.ts b/packages/client/src/schemas/table/hooks/useActionLogDetailsResource.ts
new file mode 100644
index 0000000000..d0ecefd2c5
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useActionLogDetailsResource.ts
@@ -0,0 +1,38 @@
+import { useContext, useEffect } from 'react';
+import { useTable } from './useTable';
+import useRequest from '@ahooksjs/use-request';
+import { useCollectionContext, useResourceRequest } from '../../../constate';
+import { VisibleContext } from '../../../context';
+import { TableRowContext } from '../context';
+
+export const useActionLogDetailsResource = ({ onSuccess }) => {
+ const { props } = useTable();
+ const { collection } = useCollectionContext();
+ const ctx = useContext(TableRowContext);
+ const resource = useResourceRequest({
+ resourceName: 'action_logs',
+ resourceKey: ctx.record[props.rowKey],
+ });
+ const service = useRequest(
+ (params?: any) => {
+ return resource.get({
+ ...params,
+ appends: ['changes', 'user', 'collection'],
+ });
+ },
+ {
+ formatResult: (result) => result?.data,
+ onSuccess,
+ manual: true,
+ },
+ );
+ const [visible] = useContext(VisibleContext);
+
+ useEffect(() => {
+ if (visible) {
+ service.run({});
+ }
+ }, [visible]);
+
+ return { resource, service, initialValues: service.data, ...service };
+};
diff --git a/packages/client/src/schemas/table/hooks/useActionLogsResource.ts b/packages/client/src/schemas/table/hooks/useActionLogsResource.ts
new file mode 100644
index 0000000000..a1a8e8ae38
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useActionLogsResource.ts
@@ -0,0 +1,35 @@
+import { useContext, createContext, useEffect, useState } from 'react';
+import { useTable } from './useTable';
+import { ListOptions, Resource } from '../../../resource';
+import { TableRowContext } from '../context';
+import { useResourceRequest } from '../../../constate';
+
+export const useActionLogsResource = (options: any = {}) => {
+ const { props } = useTable();
+ const ctx = useContext(TableRowContext);
+
+ class ActionLogoResource extends Resource {
+ list(options?: ListOptions) {
+ console.log({ options });
+ let defaultFilter = options?.defaultFilter;
+ if (ctx?.record) {
+ const extra = {
+ index: ctx?.record?.id,
+ collection_name: props.collectionName,
+ };
+ if (defaultFilter) {
+ defaultFilter = { and: [defaultFilter, extra] };
+ } else {
+ defaultFilter = extra;
+ }
+ }
+ return super.list({ ...options, defaultFilter });
+ }
+ }
+
+ const resource = useResourceRequest('action_logs', ActionLogoResource);
+
+ return {
+ resource,
+ };
+};
diff --git a/packages/client/src/schemas/table/hooks/useCollectionFields.ts b/packages/client/src/schemas/table/hooks/useCollectionFields.ts
new file mode 100644
index 0000000000..bf55b475df
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useCollectionFields.ts
@@ -0,0 +1,24 @@
+import { Schema } from '@formily/react';
+import { isColumn } from '../utils';
+
+export const useCollectionFields = (schema: Schema) => {
+ const columns = schema.reduceProperties((columns, current) => {
+ if (isColumn(current)) {
+ if (current['x-hidden']) {
+ return columns;
+ }
+ if (current['x-display'] && current['x-display'] !== 'visible') {
+ return columns;
+ }
+ return [...columns, current];
+ }
+ return [...columns];
+ }, []);
+
+ return columns
+ .map((column) => {
+ const columnProps = column['x-component-props'] || {};
+ return columnProps.fieldName;
+ })
+ .filter(Boolean);
+};
diff --git a/packages/client/src/schemas/table/hooks/useDataSource.ts b/packages/client/src/schemas/table/hooks/useDataSource.ts
new file mode 100644
index 0000000000..a4d10b82ba
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useDataSource.ts
@@ -0,0 +1,17 @@
+import { useTable } from './useTable';
+
+export const useDataSource = () => {
+ const {
+ pagination,
+ field,
+ props: { clientSidePagination, dataRequest },
+ } = useTable();
+ let dataSource = field.value;
+ // if (pagination && (clientSidePagination || !dataRequest)) {
+ // const { page = 1, pageSize } = pagination;
+ // const startIndex = (page - 1) * pageSize;
+ // const endIndex = startIndex + pageSize - 1;
+ // dataSource = field.value?.slice(startIndex, endIndex + 1);
+ // }
+ return dataSource;
+};
diff --git a/packages/client/src/schemas/table/hooks/useDefaultRowSelection.ts b/packages/client/src/schemas/table/hooks/useDefaultRowSelection.ts
new file mode 100644
index 0000000000..6b0deb5117
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useDefaultRowSelection.ts
@@ -0,0 +1,5 @@
+export const useDefaultRowSelection = () => {
+ return {
+ type: 'checkbox',
+ };
+};
diff --git a/packages/client/src/schemas/table/hooks/useDefaultSelectedRowKeys.ts b/packages/client/src/schemas/table/hooks/useDefaultSelectedRowKeys.ts
new file mode 100644
index 0000000000..752db958d1
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useDefaultSelectedRowKeys.ts
@@ -0,0 +1,6 @@
+import { useState } from 'react';
+
+export const useDefaultSelectedRowKeys = () => {
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+ return { selectedRowKeys, setSelectedRowKeys };
+};
diff --git a/packages/client/src/schemas/table/hooks/usePagination.ts b/packages/client/src/schemas/table/hooks/usePagination.ts
new file mode 100644
index 0000000000..66a245afbe
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/usePagination.ts
@@ -0,0 +1,33 @@
+import { ArrayField } from '@formily/core';
+import { useField } from '@formily/react';
+
+export const usePagination = () => {
+ const field = useField();
+ const paginationProps = field.componentProps.pagination;
+
+ let pagination = paginationProps;
+
+ // const [pagination, setPagination] = useState(() => {
+ // if (!paginationProps) {
+ // return false;
+ // }
+ // const { defaultPageSize = 10, ...others } = paginationProps;
+ // return { page: 1, pageSize: defaultPageSize, ...others };
+ // });
+
+ // useEffect(() => {
+ // if (!paginationProps) {
+ // return setPagination(false);
+ // }
+ // const { defaultPageSize = 10, ...others } = paginationProps;
+ // setPagination({ page: 1, pageSize: defaultPageSize, ...others });
+ // }, [paginationProps]);
+
+ return [
+ pagination,
+ (params) => {
+ const defaults = field.componentProps.pagination;
+ field.componentProps.pagination = { ...defaults, ...params };
+ },
+ ];
+};
diff --git a/packages/client/src/schemas/table/hooks/useResource.ts b/packages/client/src/schemas/table/hooks/useResource.ts
new file mode 100644
index 0000000000..24b405302b
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useResource.ts
@@ -0,0 +1,51 @@
+import { useContext } from 'react';
+import { Schema } from '@formily/react';
+import { useTable } from './useTable';
+import { TableRowContext } from '../context';
+import useRequest from '@ahooksjs/use-request';
+import { useCollectionContext, useResourceRequest } from '../../../constate';
+import { useDesignable } from '../..';
+
+export const useResource = ({ onSuccess, manual = true }) => {
+ const { props } = useTable();
+ const { collection } = useCollectionContext();
+ const ctx = useContext(TableRowContext);
+ const resource = useResourceRequest({
+ resourceName: collection?.name || props.collectionName,
+ resourceKey: ctx.record[props.rowKey],
+ });
+ const { schema } = useDesignable();
+ const fieldFields = (schema: Schema) => {
+ const names = [];
+ schema.reduceProperties((buf, current) => {
+ if (current['x-component'] === 'Form.Field') {
+ const fieldName = current['x-component-props']?.['fieldName'];
+ if (fieldName) {
+ buf.push(fieldName);
+ }
+ } else {
+ const fieldNames = fieldFields(current);
+ buf.push(...fieldNames);
+ }
+ return buf;
+ }, names);
+ return names;
+ };
+ console.log(
+ 'collection?.name || props.collectionName',
+ collection?.name || props.collectionName,
+ // fieldFields(schema),
+ );
+ const service = useRequest(
+ (params?: any) => {
+ console.log('Table.useResource', params);
+ return resource.get({ ...params, appends: fieldFields(schema) });
+ },
+ {
+ formatResult: (result) => result?.data,
+ onSuccess,
+ manual,
+ },
+ );
+ return { resource, service, initialValues: service.data, ...service };
+};
diff --git a/packages/client/src/schemas/table/hooks/useTable.ts b/packages/client/src/schemas/table/hooks/useTable.ts
new file mode 100644
index 0000000000..2f235d5e08
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTable.ts
@@ -0,0 +1,6 @@
+import { useContext } from 'react';
+import { TableContext } from '../context';
+
+export const useTable = () => {
+ return useContext(TableContext);
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableActionBars.ts b/packages/client/src/schemas/table/hooks/useTableActionBars.ts
new file mode 100644
index 0000000000..6e078898b1
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableActionBars.ts
@@ -0,0 +1,18 @@
+import { useTable } from './useTable';
+
+export const useTableActionBars = () => {
+ const {
+ field,
+ schema,
+ props: { rowKey },
+ } = useTable();
+
+ const bars = schema.reduceProperties((bars, current) => {
+ if (current['x-component'] === 'Table.ActionBar') {
+ return [...bars, current];
+ }
+ return [...bars];
+ }, []);
+
+ return bars;
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableColumns.tsx b/packages/client/src/schemas/table/hooks/useTableColumns.tsx
new file mode 100644
index 0000000000..6067afb9cd
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableColumns.tsx
@@ -0,0 +1,69 @@
+import React, { createContext } from 'react';
+import { findIndex } from 'lodash';
+import { RecursionField, Schema } from '@formily/react';
+import { useDesignable } from '../..';
+import { useTable } from './useTable';
+import { TableRowContext } from '../context';
+import { useCollectionContext } from '../../../constate';
+import { Table } from '../Table';
+import { isColumn } from '../utils';
+import { AddColumn } from '../AddColumn';
+import { CollectionFieldContext } from '../context';
+
+export const useTableColumns = () => {
+ const {
+ field,
+ schema,
+ props: { rowKey },
+ } = useTable();
+ const { designable } = useDesignable();
+
+ const { getField } = useCollectionContext();
+
+ const columnSchemas = schema.reduceProperties((columns, current) => {
+ if (isColumn(current)) {
+ if (current['x-hidden']) {
+ return columns;
+ }
+ if (current['x-display'] && current['x-display'] !== 'visible') {
+ return columns;
+ }
+ return [...columns, current];
+ }
+ return [...columns];
+ }, []);
+
+ const columns: any[] = [].concat(
+ columnSchemas.map((column: Schema) => {
+ const columnProps = column['x-component-props'] || {};
+ const collectionField = getField(columnProps.fieldName);
+ return {
+ title: (
+
+
+
+ ),
+ dataIndex: column.name,
+ ...columnProps,
+ render: (_: any, record: any) => {
+ const index = findIndex(field.value, (item) => item[rowKey] === record[rowKey]);
+ return (
+
+
+
+
+
+ );
+ },
+ };
+ }),
+ );
+
+ if (designable && schema['x-designable-bar'] && schema['x-designable-bar'] !== 'Table.SimpleDesignableBar') {
+ columns.push({
+ title: ,
+ dataIndex: 'addnew',
+ });
+ }
+ return columns;
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableCreateAction.ts b/packages/client/src/schemas/table/hooks/useTableCreateAction.ts
new file mode 100644
index 0000000000..77525ae03c
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableCreateAction.ts
@@ -0,0 +1,24 @@
+import { useForm } from '@formily/react';
+import { useTable } from './useTable';
+
+export function useTableCreateAction() {
+ const {
+ field,
+ service,
+ resource,
+ refresh,
+ props: { refreshRequestOnChange },
+ } = useTable();
+ const form = useForm();
+ return {
+ async run() {
+ console.log('refreshRequestOnChange', refreshRequestOnChange);
+ if (refreshRequestOnChange) {
+ await resource.create(form.values);
+ await form.reset();
+ return service.refresh();
+ }
+ field.unshift(form.values);
+ },
+ };
+}
diff --git a/packages/client/src/schemas/table/hooks/useTableDestroyAction.ts b/packages/client/src/schemas/table/hooks/useTableDestroyAction.ts
new file mode 100644
index 0000000000..bb7564f524
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableDestroyAction.ts
@@ -0,0 +1,49 @@
+import { useContext } from 'react';
+import { useTable } from './useTable';
+import { TableRowContext } from '../context';
+import { VisibleContext } from '../../../context';
+import { findIndex } from 'lodash';
+
+export const useTableDestroyAction = () => {
+ const {
+ resource,
+ field,
+ service,
+ selectedRowKeys,
+ setSelectedRowKeys,
+ refresh,
+ props: { refreshRequestOnChange, rowKey },
+ } = useTable();
+ const ctx = useContext(TableRowContext);
+ const [, setVisible] = useContext(VisibleContext);
+ return {
+ async run() {
+ if (refreshRequestOnChange) {
+ const rowKeys = selectedRowKeys || [];
+ if (ctx) {
+ rowKeys.push(ctx.record[rowKey]);
+ }
+ await resource.destroy({
+ [`${rowKey}.in`]: rowKeys,
+ });
+ setSelectedRowKeys([]);
+ setVisible && setVisible(false);
+ return service.refresh();
+ }
+ if (ctx) {
+ console.log('ctx.index', ctx.index);
+ field.remove(ctx.index);
+ refresh();
+ }
+ const rowKeys = [...selectedRowKeys];
+ while (rowKeys.length) {
+ const key = rowKeys.shift();
+ const index = findIndex(field.value, (item) => item[rowKey] === key);
+ field.remove(index);
+ }
+ setSelectedRowKeys([]);
+ refresh();
+ return;
+ },
+ };
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableExportAction.ts b/packages/client/src/schemas/table/hooks/useTableExportAction.ts
new file mode 100644
index 0000000000..e701c4fb70
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableExportAction.ts
@@ -0,0 +1,51 @@
+import { useField } from '@formily/react';
+import { useContext } from 'react';
+import { useTable } from './useTable';
+import { TableRowContext } from '../context';
+import { useCollectionContext } from '../../../constate';
+
+export const useTableExportAction = () => {
+ const {
+ resource,
+ field,
+ service,
+ selectedRowKeys,
+ setSelectedRowKeys,
+ refresh,
+ schema,
+ props: { refreshRequestOnChange, rowKey },
+ } = useTable();
+ const ctx = useContext(TableRowContext);
+
+ const actionField = useField();
+ const fieldNames = actionField.componentProps.fieldNames || [];
+ const { getField } = useCollectionContext();
+
+ const columns = fieldNames
+ .map((name) => {
+ const f = getField(name);
+ return {
+ title: f?.uiSchema.title,
+ name,
+ sort: f?.sort,
+ };
+ })
+ .sort((a, b) => a.sort - b.sort);
+
+ return {
+ async run() {
+ const rowKeys = selectedRowKeys || [];
+ const { filter = {}, ...others } = service.params[0];
+ if (rowKeys.length) {
+ filter[`${rowKey}.in`] = rowKeys;
+ }
+ await resource.export({
+ ...others,
+ columns,
+ perPage: -1,
+ page: 1,
+ filter,
+ });
+ },
+ };
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableFilterAction.ts b/packages/client/src/schemas/table/hooks/useTableFilterAction.ts
new file mode 100644
index 0000000000..6307d65ed8
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableFilterAction.ts
@@ -0,0 +1,23 @@
+import { useTable } from './useTable';
+import { useForm } from '@formily/react';
+
+export function useTableFilterAction() {
+ const {
+ field,
+ service,
+ refresh,
+ props: { refreshRequestOnChange },
+ } = useTable();
+ const form = useForm();
+ return {
+ async run() {
+ console.log('useTableFilterAction', form.values);
+ if (refreshRequestOnChange) {
+ return service.run({
+ ...service.params[0],
+ // filter,
+ });
+ }
+ },
+ };
+}
diff --git a/packages/client/src/schemas/table/hooks/useTableIndex.ts b/packages/client/src/schemas/table/hooks/useTableIndex.ts
new file mode 100644
index 0000000000..5134771830
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableIndex.ts
@@ -0,0 +1,16 @@
+import { useContext } from 'react';
+import { useTable } from './useTable';
+import { TableRowContext } from '../context';
+
+export const useTableIndex = () => {
+ const { pagination, props } = useTable();
+ const ctx = useContext(TableRowContext);
+ const { pageSize, page = 1 } = pagination;
+ console.log({ pageSize, page }, ctx.index);
+ return ctx.index + (page - 1) * pageSize;
+ if (pagination && !props.clientSidePagination) {
+ const { pageSize, page = 1 } = pagination;
+ return ctx.index + (page - 1) * pageSize;
+ }
+ return ctx.index;
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableRow.ts b/packages/client/src/schemas/table/hooks/useTableRow.ts
new file mode 100644
index 0000000000..ec88b96cb3
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableRow.ts
@@ -0,0 +1,6 @@
+import React, { useContext } from 'react';
+import { TableRowContext } from '../context';
+
+export const useTableRow = () => {
+ return useContext(TableRowContext);
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableRowRecord.ts b/packages/client/src/schemas/table/hooks/useTableRowRecord.ts
new file mode 100644
index 0000000000..d2c9125f69
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableRowRecord.ts
@@ -0,0 +1,7 @@
+import { useContext } from 'react';
+import { TableRowContext } from '../context';
+
+export const useTableRowRecord = () => {
+ const ctx = useContext(TableRowContext);
+ return ctx.record;
+};
diff --git a/packages/client/src/schemas/table/hooks/useTableUpdateAction.ts b/packages/client/src/schemas/table/hooks/useTableUpdateAction.ts
new file mode 100644
index 0000000000..3ec4976625
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTableUpdateAction.ts
@@ -0,0 +1,35 @@
+import { useForm } from '@formily/react';
+import { useContext } from 'react';
+import { useTable } from './useTable';
+import { TableRowContext } from '../context';
+import { DescriptionsContext } from '../../form';
+
+export const useTableUpdateAction = () => {
+ const {
+ resource,
+ field,
+ service,
+ refresh,
+ props: { refreshRequestOnChange, rowKey },
+ } = useTable();
+ const ctx = useContext(TableRowContext);
+ const form = useForm();
+ const { service: formService } = useContext(DescriptionsContext);
+
+ return {
+ async run() {
+ if (refreshRequestOnChange) {
+ await resource.save(form.values, {
+ resourceKey: ctx.record[rowKey],
+ });
+ if (formService) {
+ await formService.refresh();
+ }
+ await service.refresh();
+ return;
+ }
+ field.value[ctx.index] = form.values;
+ // refresh();
+ },
+ };
+};
diff --git a/packages/client/src/schemas/table/hooks/useTotal.ts b/packages/client/src/schemas/table/hooks/useTotal.ts
new file mode 100644
index 0000000000..eb7c298516
--- /dev/null
+++ b/packages/client/src/schemas/table/hooks/useTotal.ts
@@ -0,0 +1,10 @@
+import { useTable } from './useTable';
+
+export const useTotal = () => {
+ const {
+ field,
+ service,
+ props: { clientSidePagination },
+ } = useTable();
+ return clientSidePagination ? field?.value?.length : service?.data?.total;
+};
diff --git a/packages/client/src/schemas/table/index.ts b/packages/client/src/schemas/table/index.ts
new file mode 100644
index 0000000000..919ad6ea08
--- /dev/null
+++ b/packages/client/src/schemas/table/index.ts
@@ -0,0 +1,5 @@
+export * from './Table';
+export * from './context';
+
+//hooks export
+export * from './hooks/useTable';
diff --git a/packages/client/src/schemas/table/index.tsx b/packages/client/src/schemas/table/index.tsx
deleted file mode 100644
index 75ef11bc5c..0000000000
--- a/packages/client/src/schemas/table/index.tsx
+++ /dev/null
@@ -1,2665 +0,0 @@
-import {
- FormProvider,
- observer,
- RecursionField,
- Schema,
- useField,
- useForm,
-} from '@formily/react';
-import { Modal, Pagination, Popover, Table as AntdTable } from 'antd';
-import { cloneDeep, cloneDeepWith, findIndex, forIn, range, set } from 'lodash';
-import React, { Fragment, useEffect, useState } from 'react';
-import { useContext } from 'react';
-import { createContext } from 'react';
-import { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect';
-import { useDesignable, createCollectionField, ISchema } from '..';
-import { uid, merge } from '@formily/shared';
-import useRequest from '@ahooksjs/use-request';
-import { BaseResult } from '@ahooksjs/use-request/lib/types';
-import cls from 'classnames';
-import { MenuOutlined, DragOutlined, FilterOutlined } from '@ant-design/icons';
-import { DndContext, DragOverlay } from '@dnd-kit/core';
-import {
- SortableContext,
- useSortable,
- verticalListSortingStrategy,
-} from '@dnd-kit/sortable';
-import { Select, Dropdown, Menu, Switch, Button, Space } from 'antd';
-import { PlusOutlined, SettingOutlined } from '@ant-design/icons';
-import './style.less';
-import {
- findPropertyByPath,
- getSchemaPath,
- SchemaField,
- SchemaRenderer,
-} from '../../components/schema-renderer';
-import {
- interfaces,
- isAssociation,
- options,
-} from '../database-field/interfaces';
-import { DraggableBlockContext } from '../../components/drag-and-drop';
-import AddNew from '../add-new';
-import { isGridRowOrCol } from '../grid';
-import { ListOptions, Resource } from '../../resource';
-import {
- CollectionProvider,
- DisplayedMapProvider,
- useCollection,
- useCollectionContext,
- useCollectionsContext,
- useDisplayedMapContext,
- useClient,
- useResourceRequest,
-} from '../../constate';
-import { useResource as useGeneralResource } from '../../hooks/useResource';
-import SwitchMenuItem from '../../components/SwitchMenuItem';
-import { useMemo } from 'react';
-import { ArrayField, createForm,LifeCycleTypes } from '@formily/core';
-import {
- ColDraggableContext,
- SortableBodyCell,
- SortableBodyRow,
- SortableColumn,
- SortableHeaderCell,
- SortableHeaderRow,
- SortableRowHandle,
-} from './Sortable';
-import { DragHandle, Droppable, SortableItem } from '../../components/Sortable';
-import { isValid } from '@formily/shared';
-import { FormButtonGroup, FormDialog, FormLayout, Submit } from '@formily/antd';
-import flatten from 'flat';
-import IconPicker from '../../components/icon-picker';
-import { DescriptionsContext } from '../form';
-import { VisibleContext } from '../../context';
-import { SimpleDesignableBar } from './SimpleDesignableBar';
-import { Trans, useTranslation } from 'react-i18next';
-import { useCompile } from '../../hooks/useCompile';
-
-export interface ITableContext {
- props: any;
- field: ArrayField;
- schema: Schema;
- service: BaseResult;
- selectedRowKeys?: any;
- setSelectedRowKeys?: any;
- pagination?: any;
- setPagination?: any;
- refresh?: any;
- resource?: Resource;
-}
-
-export interface ITableRowContext {
- index: number;
- record: any;
-}
-
-const TableContext = createContext({} as any);
-export const TableRowContext = createContext(null);
-export const CollectionFieldContext = createContext(null);
-
-export const useTable = () => {
- return useContext(TableContext);
-};
-
-const useTableRow = () => {
- return useContext(TableRowContext);
-};
-
-function useTableFilterAction() {
- const {
- field,
- service,
- refresh,
- props: { refreshRequestOnChange },
- } = useTable();
- const form = useForm();
- return {
- async run() {
- console.log('useTableFilterAction', form.values);
- if (refreshRequestOnChange) {
- return service.run({
- ...service.params[0],
- // filter,
- });
- }
- },
- };
-}
-
-function useTableCreateAction() {
- const {
- field,
- service,
- resource,
- refresh,
- props: { refreshRequestOnChange },
- } = useTable();
- const form = useForm();
- return {
- async run() {
- console.log('refreshRequestOnChange', refreshRequestOnChange);
- if (refreshRequestOnChange) {
- await resource.create(form.values);
- await form.reset();
- return service.refresh();
- }
- field.unshift(form.values);
- },
- };
-}
-
-const useTableUpdateAction = () => {
- const {
- resource,
- field,
- service,
- refresh,
- props: { refreshRequestOnChange, rowKey },
- } = useTable();
- const ctx = useContext(TableRowContext);
- const form = useForm();
- const { service: formService } = useContext(DescriptionsContext);
-
- return {
- async run() {
- if (refreshRequestOnChange) {
- await resource.save(form.values, {
- resourceKey: ctx.record[rowKey],
- });
- if (formService) {
- await formService.refresh();
- }
- await service.refresh();
- return;
- }
- field.value[ctx.index] = form.values;
- // refresh();
- },
- };
-};
-
-const useTableDestroyAction = () => {
- const {
- resource,
- field,
- service,
- selectedRowKeys,
- setSelectedRowKeys,
- refresh,
- props: { refreshRequestOnChange, rowKey },
- } = useTable();
- const ctx = useContext(TableRowContext);
- const [, setVisible] = useContext(VisibleContext);
- return {
- async run() {
- if (refreshRequestOnChange) {
- const rowKeys = selectedRowKeys || [];
- if (ctx) {
- rowKeys.push(ctx.record[rowKey]);
- }
- await resource.destroy({
- [`${rowKey}.in`]: rowKeys,
- });
- setSelectedRowKeys([]);
- setVisible && setVisible(false);
- return service.refresh();
- }
- if (ctx) {
- console.log('ctx.index', ctx.index);
- field.remove(ctx.index);
- refresh();
- }
- const rowKeys = [...selectedRowKeys];
- while (rowKeys.length) {
- const key = rowKeys.shift();
- const index = findIndex(field.value, (item) => item[rowKey] === key);
- field.remove(index);
- }
- setSelectedRowKeys([]);
- refresh();
- return;
- },
- };
-};
-
-const useTableExportAction = () => {
- const {
- resource,
- field,
- service,
- selectedRowKeys,
- setSelectedRowKeys,
- refresh,
- schema,
- props: { refreshRequestOnChange, rowKey },
- } = useTable();
- const ctx = useContext(TableRowContext);
-
- const actionField = useField();
- const fieldNames = actionField.componentProps.fieldNames || [];
- const { getField } = useCollectionContext();
-
- const columns = fieldNames
- .map((name) => {
- const f = getField(name);
- return {
- title: f?.uiSchema.title,
- name,
- sort: f?.sort,
- };
- })
- .sort((a, b) => a.sort - b.sort);
-
- return {
- async run() {
- const rowKeys = selectedRowKeys || [];
- const { filter = {}, ...others } = service.params[0];
- if (rowKeys.length) {
- filter[`${rowKey}.in`] = rowKeys;
- }
- await resource.export({
- ...others,
- columns,
- perPage: -1,
- page: 1,
- filter,
- });
- },
- };
-};
-
-const useTableRowRecord = () => {
- const ctx = useContext(TableRowContext);
- return ctx.record;
-};
-
-const useTableIndex = () => {
- const { pagination, props } = useTable();
- const ctx = useContext(TableRowContext);
- const { pageSize, page = 1 } = pagination;
- console.log({ pageSize, page }, ctx.index);
- return ctx.index + (page - 1) * pageSize;
- if (pagination && !props.clientSidePagination) {
- const { pageSize, page = 1 } = pagination;
- return ctx.index + (page - 1) * pageSize;
- }
- return ctx.index;
-};
-
-const useTableActionBars = () => {
- const {
- field,
- schema,
- props: { rowKey },
- } = useTable();
-
- const bars = schema.reduceProperties((bars, current) => {
- if (current['x-component'] === 'Table.ActionBar') {
- return [...bars, current];
- }
- return [...bars];
- }, []);
-
- return bars;
-};
-
-export function isOperationColumn(schema: Schema) {
- return ['Table.Operation'].includes(schema['x-component']);
-}
-
-export function isColumn(schema: Schema) {
- return ['Table.Column'].includes(schema['x-component']);
-}
-
-export function isColumnComponent(component: string) {
- return ['Table.Operation', 'Table.Column'].includes(component);
-}
-
-const useCollectionFields = (schema: Schema) => {
- const columns = schema.reduceProperties((columns, current) => {
- if (isColumn(current)) {
- if (current['x-hidden']) {
- return columns;
- }
- if (current['x-display'] && current['x-display'] !== 'visible') {
- return columns;
- }
- return [...columns, current];
- }
- return [...columns];
- }, []);
-
- return columns
- .map((column) => {
- const columnProps = column['x-component-props'] || {};
- return columnProps.fieldName;
- })
- .filter(Boolean);
-};
-
-const useTableColumns = () => {
- const {
- field,
- schema,
- props: { rowKey },
- } = useTable();
- const { designable } = useDesignable();
-
- const { getField } = useCollectionContext();
-
- const columnSchemas = schema.reduceProperties((columns, current) => {
- if (isColumn(current)) {
- if (current['x-hidden']) {
- return columns;
- }
- if (current['x-display'] && current['x-display'] !== 'visible') {
- return columns;
- }
- return [...columns, current];
- }
- return [...columns];
- }, []);
-
- const columns: any[] = [].concat(
- columnSchemas.map((column: Schema) => {
- const columnProps = column['x-component-props'] || {};
- const collectionField = getField(columnProps.fieldName);
- return {
- title: (
-
-
-
- ),
- dataIndex: column.name,
- ...columnProps,
- render: (_: any, record: any) => {
- const index = findIndex(
- field.value,
- (item) => item[rowKey] === record[rowKey],
- );
- return (
-
-
-
-
-
- );
- },
- };
- }),
- );
-
- if (
- designable &&
- schema['x-designable-bar'] &&
- schema['x-designable-bar'] !== 'Table.SimpleDesignableBar'
- ) {
- columns.push({
- title: ,
- dataIndex: 'addnew',
- });
- }
- return columns;
-};
-
-function AddColumn() {
- const { t } = useTranslation();
- const [visible, setVisible] = useState(false);
- const { appendChild, remove } = useDesignable();
- const { loadCollections } = useCollectionsContext();
- const { collection, fields, refresh } = useCollectionContext();
- const displayed = useDisplayedMapContext();
- const { service } = useTable();
- const { createSchema, removeSchema, updateSchema } = useClient();
- return (
-
-
- {fields.map((field) => (
- {
- if (checked) {
- console.log(
- 'SwitchMenuItem.field.name',
- field.dataType,
- service.params[0],
- );
- const columnSchema: ISchema = {
- type: 'void',
- 'x-component': 'Table.Column',
- 'x-component-props': {
- fieldName: field.name,
- },
- 'x-designable-bar': 'Table.Column.DesignableBar',
- };
- if (field.interface === 'linkTo') {
- columnSchema.properties = {
- options: {
- type: 'void',
- 'x-decorator': 'Form',
- 'x-component': 'Select.Options.Drawer',
- 'x-component-props': {
- useOkAction: '{{ Select.useOkAction }}',
- },
- title: "{{t('Select record')}}",
- properties: {
- table: {
- type: 'array',
- 'x-designable-bar': 'Table.DesignableBar',
- 'x-decorator': 'BlockItem',
- 'x-decorator-props': {
- draggable: false,
- },
- 'x-component': 'Table',
- default: [],
- 'x-component-props': {
- rowKey: 'id',
- useRowSelection: '{{ Select.useRowSelection }}',
- useSelectedRowKeys:
- '{{ Select.useSelectedRowKeys }}',
- onSelect: '{{ Select.useSelect() }}',
- collectionName: field.target,
- // dragSort: true,
- // showIndex: true,
- refreshRequestOnChange: true,
- pagination: {
- pageSize: 10,
- },
- },
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Table.ActionBar',
- 'x-designable-bar':
- 'Table.ActionBar.DesignableBar',
- properties: {
- [uid()]: {
- type: 'void',
- title: "{{t('Filter')}}",
- 'x-decorator': 'AddNew.Displayed',
- 'x-decorator-props': {
- displayName: 'filter',
- },
- 'x-align': 'left',
- 'x-component': 'Table.Filter',
- 'x-designable-bar':
- 'Table.Filter.DesignableBar',
- 'x-component-props': {
- fieldNames: [],
- },
- },
- },
- },
- },
- },
- },
- },
- option: {
- type: 'void',
- 'x-component': 'Select.OptionTag',
- properties: {
- [uid()]: {
- type: 'void',
- title: "{{t('View record')}}",
- 'x-component': 'Action.Drawer',
- 'x-component-props': {
- bodyStyle: {
- background: '#f0f2f5',
- },
- },
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Tabs',
- 'x-designable-bar': 'Tabs.DesignableBar',
- properties: {
- [uid()]: {
- type: 'void',
- title: "{{t('Details')}}",
- 'x-designable-bar':
- 'Tabs.TabPane.DesignableBar',
- 'x-component': 'Tabs.TabPane',
- 'x-component-props': {},
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Grid',
- 'x-component-props': {
- addNewComponent: 'AddNew.PaneItem',
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- };
- }
- const data = appendChild(columnSchema);
- await createSchema(data);
- if (isAssociation(field)) {
- const defaultAppends =
- service.params[0]?.defaultAppends || [];
- defaultAppends.push(field.name);
- await service.run({
- ...service.params[0],
- defaultAppends,
- });
- }
- } else {
- const s: any = displayed.get(field.name);
- const p = getSchemaPath(s);
- const removed = remove(p);
- await removeSchema(removed);
- displayed.remove(field.name);
- if (isAssociation(field)) {
- const defaultAppends =
- service.params[0]?.defaultAppends || [];
- const index = defaultAppends.indexOf(field.name);
- if (index > -1) {
- defaultAppends.splice(index, 1);
- }
- await service.run({
- ...service.params[0],
- defaultAppends,
- });
- }
- }
- // service.refresh();
- }}
- />
- ))}
-
-
-
- {options.map((option) => (
-
- {option.children.map((item) => (
- {
- setVisible(false);
- const values = await FormDialog(t('Add field'), () => {
- return (
-
-
-
- );
- }).open({
- initialValues: {
- interface: item.name,
- ...item.default,
- key: uid(),
- name: `f_${uid()}`,
- },
- });
- await createCollectionField(collection?.name, values);
- const data = appendChild({
- type: 'void',
- 'x-component': 'Table.Column',
- 'x-component-props': {
- fieldName: values.name,
- },
- 'x-designable-bar': 'Table.Column.DesignableBar',
- });
- await createSchema(data);
- await refresh();
- }}
- >
- {item.title}
-
- ))}
-
- ))}
-
-
- }
- >
- }
- >
- {t('Configure fields')}
-
-
- );
-}
-
-const useDefaultRowSelection = () => {
- return {
- type: 'checkbox',
- };
-};
-
-const useDataSource = () => {
- const {
- pagination,
- field,
- props: { clientSidePagination, dataRequest },
- } = useTable();
- let dataSource = field.value;
- // if (pagination && (clientSidePagination || !dataRequest)) {
- // const { page = 1, pageSize } = pagination;
- // const startIndex = (page - 1) * pageSize;
- // const endIndex = startIndex + pageSize - 1;
- // dataSource = field.value?.slice(startIndex, endIndex + 1);
- // }
- return dataSource;
-};
-
-const TableMain = () => {
- const {
- resource,
- selectedRowKeys,
- setSelectedRowKeys,
- service,
- field,
- props: {
- rowKey,
- dragSort,
- showIndex,
- onSelect,
- useRowSelection = useDefaultRowSelection,
- },
- refresh,
- } = useTable();
- const columns = useTableColumns();
- const dataSource = useDataSource();
- const actionBars = useTableActionBars();
- const [html, setHtml] = useState('');
- const { type } = useRowSelection();
- return (
-
-
{
- const fromId = event.active?.id as any;
- const toId = event.over?.id as any;
- if (isValid(fromId) && isValid(toId)) {
- const fromIndex = findIndex(
- field.value,
- (item) => item[rowKey] === fromId,
- );
- const toIndex = findIndex(
- field.value,
- (item) => item[rowKey] === toId,
- );
- console.log({ fromId, toId, fromIndex, toIndex });
- field.move(fromIndex, toIndex);
- refresh();
- await resource.sort({
- resourceKey: fromId,
- target: {
- [rowKey]: toId,
- },
- });
- await service.refresh();
- }
- }}
- >
- {actionBars.map((actionBar) => (
-
- ))}
-
- {}}
- loading={service?.loading}
- rowKey={rowKey}
- dataSource={dataSource}
- columns={columns}
- // components={{
- // body: {
- // row: DragableBodyRow,
- // },
- // }}
- components={{
- header: {
- row: SortableHeaderRow,
- cell: SortableHeaderCell,
- },
- body: {
- // wrapper: (props) => {
- // return (
- //
- //
- //
- //
- // {props.children}
- //
- // );
- // },
- row: SortableBodyRow,
- // cell: SortableBodyCell,
- },
- }}
- rowSelection={{
- type: type || 'checkbox',
- selectedRowKeys,
- onChange: (rowKeys, rows) => {
- setSelectedRowKeys(rowKeys);
- onSelect && onSelect(rowKeys, rows);
- },
- renderCell: (checked, record, _, originNode) => {
- const index = findIndex(
- field.value,
- (item) => item[rowKey] === record[rowKey],
- );
- return (
-
-
- {dragSort &&
}
- {showIndex &&
}
- {originNode}
-
-
- );
- },
- }}
- />
-
-
-
-
- );
-};
-
-const usePagination = () => {
- const field = useField();
- const paginationProps = field.componentProps.pagination;
-
- let pagination = paginationProps;
-
- // const [pagination, setPagination] = useState(() => {
- // if (!paginationProps) {
- // return false;
- // }
- // const { defaultPageSize = 10, ...others } = paginationProps;
- // return { page: 1, pageSize: defaultPageSize, ...others };
- // });
-
- // useEffect(() => {
- // if (!paginationProps) {
- // return setPagination(false);
- // }
- // const { defaultPageSize = 10, ...others } = paginationProps;
- // setPagination({ page: 1, pageSize: defaultPageSize, ...others });
- // }, [paginationProps]);
-
- return [
- pagination,
- (params) => {
- const defaults = field.componentProps.pagination;
- field.componentProps.pagination = { ...defaults, ...params };
- },
- ];
-};
-
-const useDefaultSelectedRowKeys = () => {
- const [selectedRowKeys, setSelectedRowKeys] = useState([]);
- return { selectedRowKeys, setSelectedRowKeys };
-};
-
-const TableProvider = (props: any) => {
- const {
- rowKey = 'id',
- dataRequest,
- useResource = useGeneralResource,
- defaultSelectedRowKeys,
- useSelectedRowKeys = useDefaultSelectedRowKeys,
- ...others
- } = props;
- const { schema } = useDesignable();
- const field = useField();
- const [pagination, setPagination] = usePagination();
- const { selectedRowKeys, setSelectedRowKeys } = useSelectedRowKeys();
- console.log('props.useSelectedRowKeys', selectedRowKeys);
- const [, refresh] = useState(uid());
- const { resource } = useResource();
- const { sortableField } = useCollectionContext();
- const dragSort = props.dragSort;
- const collectionFields = useCollectionFields(schema);
- // console.log({ collectionFields, pagination });
- const getDefaultParams = () => {
- const defaultParams = { ...pagination };
- if (dragSort) {
- defaultParams['sort'] = [sortableField || 'sort'];
- } else {
- defaultParams['sort'] = (props.defaultSort || []).join(',');
- }
- defaultParams['defaultAppends'] = [
- ...(props.defaultAppends || []),
- ...collectionFields,
- ];
- if (props.defaultFilter) {
- defaultParams['defaultFilter'] = props.defaultFilter;
- }
- console.log({ defaultParams });
- return defaultParams;
- };
- const service = useRequest(
- (params?: any) => {
- if (!resource) {
- return Promise.resolve({
- list: field.value,
- total: field?.value?.length,
- });
- }
- return resource.list(params).then((res) => {
- return {
- list: res?.data || [],
- total: res?.meta?.count || res?.data?.length,
- };
- });
- },
- {
- onSuccess(data: any) {
- field.setValue(data?.list || []);
- },
- manual: true,
- // defaultParams: [getDefaultParams()],
- },
- );
- useDeepCompareEffectNoCheck(() => {
- service.run(getDefaultParams());
- }, [
- pagination?.pageSize,
- pagination?.page,
- props.dragSort,
- props.defaultSort,
- props.defaultFilter,
- ]);
- return (
- {
- const { page = 1, pageSize } = pagination;
- const total = props.clientSidePagination
- ? field?.value?.length
- : service?.data?.total;
- const maxPage = Math.ceil(total / pageSize);
- if (page > maxPage) {
- setPagination({ page: maxPage });
- } else {
- refresh(uid());
- }
- },
- selectedRowKeys,
- setSelectedRowKeys,
- pagination,
- setPagination,
- service,
- field,
- schema,
- props: { ...others, rowKey, dataRequest },
- }}
- >
-
-
- );
-};
-
-export const Table: any = observer((props: any) => {
- const [visible, setVisible] = useState(false);
- return (
-
-
-
-
-
- );
-});
-
-const useTotal = () => {
- const {
- field,
- service,
- props: { clientSidePagination },
- } = useTable();
- return clientSidePagination ? field?.value?.length : service?.data?.total;
-};
-
-Table.Pagination = observer(() => {
- const { service, pagination, setPagination, props } = useTable();
- if (!pagination || Object.keys(pagination).length === 0) {
- return null;
- }
- const { clientSidePagination } = props;
- const total = useTotal();
- const { page = 1 } = pagination;
- return (
-
-
{
- const page = pagination.pageSize !== pageSize ? 1 : current;
- setPagination({
- page,
- pageSize,
- });
- // if (clientSidePagination) {
- // return;
- // }
- // service.run({
- // ...service.params[0],
- // page,
- // pageSize,
- // });
- }}
- />
-
- );
-});
-
-function generateActionSchema(type) {
- const actions: { [key: string]: ISchema } = {
- filter: {
- key: uid(),
- name: uid(),
- type: 'void',
- title: "{{ t('Filter') }}",
- 'x-align': 'left',
- 'x-decorator': 'AddNew.Displayed',
- 'x-decorator-props': {
- displayName: 'filter',
- },
- 'x-component': 'Table.Filter',
- 'x-designable-bar': 'Table.Filter.DesignableBar',
- 'x-component-props': {
- fieldNames: [],
- },
- },
- export: {
- key: uid(),
- type: 'void',
- name: uid(),
- title: "{{ t('Export') }}",
- 'x-align': 'right',
- 'x-decorator': 'AddNew.Displayed',
- 'x-decorator-props': {
- displayName: 'export',
- },
- 'x-action-type': 'export',
- 'x-component': 'Action',
- 'x-designable-bar': 'Table.ExportActionDesignableBar',
- 'x-component-props': {
- fieldNames: [],
- icon: 'ExportOutlined',
- useAction: '{{ Table.useTableExportAction }}',
- },
- },
- create: {
- key: uid(),
- type: 'void',
- name: uid(),
- title: "{{ t('Add new') }}",
- 'x-align': 'right',
- 'x-decorator': 'AddNew.Displayed',
- 'x-decorator-props': {
- displayName: 'create',
- },
- 'x-component': 'Action',
- 'x-component-props': {
- type: 'primary',
- icon: 'PlusOutlined',
- },
- 'x-designable-bar': 'Table.Action.DesignableBar',
- properties: {
- modal: {
- type: 'void',
- title: "{{ t('Add record') }}",
- 'x-decorator': 'Form',
- 'x-component': 'Action.Drawer',
- 'x-component-props': {
- useOkAction: '{{ Table.useTableCreateAction }}',
- },
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Grid',
- 'x-component-props': {
- addNewComponent: 'AddNew.FormItem',
- },
- },
- },
- },
- },
- },
- destroy: {
- key: uid(),
- type: 'void',
- name: uid(),
- title: "{{ t('Delete') }}",
- 'x-align': 'right',
- 'x-decorator': 'AddNew.Displayed',
- 'x-decorator-props': {
- displayName: 'destroy',
- },
- 'x-action-type': 'destroy',
- 'x-component': 'Action',
- 'x-designable-bar': 'Table.Action.DesignableBar',
- 'x-component-props': {
- confirm: {
- title: "{{ t('Delete record') }}",
- content: "{{ t('Are you sure you want to delete it?') }}",
- },
- useAction: '{{ Table.useTableDestroyAction }}',
- },
- },
- view: {},
- update: {},
- };
- return actions[type];
-}
-
-function generateMenuActionSchema(type) {
- const actions: { [key: string]: ISchema } = {
- view: {
- key: uid(),
- name: uid(),
- type: 'void',
- title: "{{ t('View') }}",
- 'x-component': 'Action',
- 'x-component-props': {
- type: 'link',
- },
- 'x-designable-bar': 'Table.Action.DesignableBar',
- 'x-action-type': 'view',
- properties: {
- [uid()]: {
- type: 'void',
- title: "{{ t('View record') }}",
- 'x-component': 'Action.Drawer',
- 'x-component-props': {
- bodyStyle: {
- background: '#f0f2f5',
- // paddingTop: 0,
- },
- },
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Tabs',
- 'x-designable-bar': 'Tabs.DesignableBar',
- properties: {
- [uid()]: {
- type: 'void',
- title: "{{ t('Details') }}",
- 'x-designable-bar': 'Tabs.TabPane.DesignableBar',
- 'x-component': 'Tabs.TabPane',
- 'x-component-props': {},
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Grid',
- 'x-component-props': {
- addNewComponent: 'AddNew.PaneItem',
- },
- },
- },
- },
- },
- },
- },
- },
- },
- },
- update: {
- key: uid(),
- name: uid(),
- type: 'void',
- title: "{{ t('Edit') }}",
- 'x-component': 'Action',
- 'x-component-props': {
- type: 'link',
- },
- 'x-designable-bar': 'Table.Action.DesignableBar',
- 'x-action-type': 'update',
- properties: {
- [uid()]: {
- type: 'void',
- title: "{{ t('Edit record') }}",
- 'x-decorator': 'Form',
- 'x-decorator-props': {
- useResource: '{{ Table.useResource }}',
- useValues: '{{ Table.useTableRowRecord }}',
- },
- 'x-component': 'Action.Drawer',
- 'x-component-props': {
- useOkAction: '{{ Table.useTableUpdateAction }}',
- },
- properties: {
- [uid()]: {
- type: 'void',
- 'x-component': 'Grid',
- 'x-component-props': {
- addNewComponent: 'AddNew.FormItem',
- },
- },
- },
- },
- },
- },
- destroy: {
- key: uid(),
- name: uid(),
- type: 'void',
- title: "{{ t('Delete') }}",
- 'x-component': 'Action',
- 'x-designable-bar': 'Table.Action.DesignableBar',
- 'x-action-type': 'destroy',
- 'x-component-props': {
- useAction: '{{ Table.useTableDestroyAction }}',
- type: 'link',
- confirm: {
- title: "{{ t('Delete record') }}",
- content: "{{ t('Are you sure you want to delete it?') }}",
- },
- },
- },
- };
- return actions[type];
-}
-
-function AddActionButton() {
- const { t } = useTranslation();
- const [visible, setVisible] = useState(false);
- const displayed = useDisplayedMapContext();
- const { appendChild, remove } = useDesignable();
- const { schema, designable } = useDesignable();
- const { createSchema, removeSchema, updateSchema } = useClient();
-
- if (!designable || !schema['x-designable-bar']) {
- return null;
- }
- return (
-
-
- {[
- { title: t('Filter'), name: 'filter' },
- { title: t('Export'), name: 'export' },
- { title: t('Add new'), name: 'create' },
- { title: t('Delete'), name: 'destroy' },
- ].map((item) => (
- {
- if (!checked) {
- const s = displayed.get(item.name) as Schema;
- const path = getSchemaPath(s);
- displayed.remove(item.name);
- const removed = remove(path);
- await removeSchema(removed);
- } else {
- const s = generateActionSchema(item.name);
- const data = appendChild(s);
- await createSchema(data);
- }
- }}
- />
- ))}
-
-
-
- {t('Function')}
- {t('Popup form')}
- {t('Flexible popup')}
-
-
- }
- >
- }
- >
- {t('Configure actions')}
-
-
- );
-}
-
-function Actions(props: any) {
- const { align = 'left' } = props;
- const { schema, designable } = useDesignable();
- return (
-
-
- {schema.mapProperties((s) => {
- const currentAlign = s['x-align'] || 'left';
- if (currentAlign !== align) {
- return null;
- }
- return (
-
-
-
- );
- })}
-
-
- );
-}
-
-Table.ActionBar = observer((props: any) => {
- const { align = 'top' } = props;
- // const { schema, designable } = useDesignable();
- const { root, schema, insertAfter, remove, appendChild } = useDesignable();
- const moveToAfter = (path1, path2, extra = {}) => {
- if (!path1 || !path2) {
- return;
- }
- if (path1.join('.') === path2.join('.')) {
- return;
- }
- const data = findPropertyByPath(root, path1);
- if (!data) {
- return;
- }
- remove(path1);
- return insertAfter(
- {
- ...data.toJSON(),
- ...extra,
- },
- path2,
- );
- };
- const { createSchema, removeSchema, updateSchema } = useClient();
-
- const [dragOverlayContent, setDragOverlayContent] = useState('');
- return (
- {
- setDragOverlayContent(event.active.data?.current?.title || '');
- // const previewRef = event.active.data?.current?.previewRef;
- // if (previewRef) {
- // setDragOverlayContent(previewRef?.current?.innerHTML);
- // } else {
- // setDragOverlayContent('');
- // }
- }}
- onDragEnd={async (event) => {
- const path1 = event.active?.data?.current?.path;
- const path2 = event.over?.data?.current?.path;
- const align = event.over?.data?.current?.align;
- const draggable = event.over?.data?.current?.draggable;
- if (!path1 || !path2) {
- return;
- }
- if (path1.join('.') === path2.join('.')) {
- return;
- }
- if (!draggable) {
- console.log('alignalignalignalign', align);
- const p = findPropertyByPath(root, path1);
- if (!p) {
- return;
- }
- remove(path1);
- const data = appendChild(
- {
- ...p.toJSON(),
- 'x-align': align,
- },
- path2,
- );
- await updateSchema(data);
- } else {
- const data = moveToAfter(path1, path2, {
- 'x-align': align,
- });
- await updateSchema(data);
- }
- }}
- >
-
- {dragOverlayContent}
- {/* */}
-
-
-
-
-
- );
-});
-
-const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
- const { fieldNames = [] } = options;
- const properties = {};
- fields.forEach((field, index) => {
- if (fieldNames?.length && !fieldNames.includes(field.name)) {
- return;
- }
- const fieldOption = interfaces.get(field.interface);
- if (!fieldOption?.operations) {
- return;
- }
- properties[`column${index}`] = {
- type: 'void',
- title: field?.uiSchema?.title,
- 'x-component': 'Filter.Column',
- 'x-component-props': {
- operations: fieldOption.operations,
- },
- properties: {
- [field.name]: {
- ...field.uiSchema,
- 'x-decorator': 'FormilyFormItem',
- title: null,
- },
- },
- };
- });
- return properties;
-};
-
-const fieldsToSortColumns = (fields: any[]) => {
- const dataSource = [];
-
- fields.forEach((field) => {
- const fieldOption = interfaces.get(field.interface);
- if (!fieldOption?.sortable) {
- return;
- }
- dataSource.push({
- value: field.name,
- label: field?.uiSchema?.title,
- });
- });
-
- return dataSource;
-};
-
-Table.Filter = observer((props: any) => {
- const { service } = useTable();
- const { fieldNames = [] } = props;
- const compile = useCompile();
- const { t } = useTranslation();
- const { schema, DesignableBar } = useDesignable();
- const form = useMemo(() => createForm(), []);
- const { fields = [] } = useCollectionContext();
- const [visible, setVisible] = useState(false);
- const obj = flatten(form.values.filter || {});
- console.log('flatten', obj, Object.values(obj));
- const count = Object.values(obj).filter((i) =>
- Array.isArray(i) ? i.length : i,
- ).length;
- const icon = props.icon || 'FilterOutlined';
- const properties = fieldsToFilterColumns(fields, { fieldNames });
- schema.mapProperties((p) => {
- properties[p.name] = p;
- });
- return (
-
-
-
-
-
- {
- const { filter } = form.values;
- console.log('Table.Filter', form.values);
- setVisible(false);
- return service.run({
- ...service.params[0],
- filter,
- });
- }}
- >
- {t('Submit')}
-
-
-
-
- }
- >
- }>
- {count > 0 ? t('{{count}} filter items', { count }) : compile(schema.title)}
-
-
-
- );
-});
-
-Table.Filter.DesignableBar = () => {
- const { t } = useTranslation();
- const { schema, remove, refresh, insertAfter } = useDesignable();
- const [visible, setVisible] = useState(false);
- const displayed = useDisplayedMapContext();
- const { fields } = useCollectionContext();
- const field = useField();
- const { createSchema, removeSchema, updateSchema } = useClient();
- let fieldNames = field.componentProps.fieldNames || [];
- if (fieldNames.length === 0) {
- fieldNames = fields.map((field) => field.name);
- }
- return (
-
- {
- e.stopPropagation();
- }}
- className={cls('designable-bar-actions', { active: visible })}
- >
-
-
- {
- setVisible(visible);
- }}
- overlay={
-
- }
- >
-
-
-
-
-
- );
-};
-
-Table.ExportActionDesignableBar = () => {
- const { t } = useTranslation();
- const { schema, remove, refresh, insertAfter } = useDesignable();
- const [visible, setVisible] = useState(false);
- const displayed = useDisplayedMapContext();
- const { fields } = useCollectionContext();
- const field = useField();
- const { createSchema, removeSchema, updateSchema } = useClient();
- let fieldNames = field.componentProps.fieldNames || [];
- if (fieldNames.length === 0) {
- fieldNames = fields.map((field) => field.name);
- }
- return (
-
- {
- e.stopPropagation();
- }}
- className={cls('designable-bar-actions', { active: visible })}
- >
-
-
- {
- setVisible(visible);
- }}
- overlay={
-
- }
- >
-
-
-
-
-
- );
-};
-
-Table.Operation = observer((props: any) => {
- const { designable, schema } = useDesignable();
- const { t } = useTranslation();
- return (
-
- );
-});
-
-Table.Operation.Cell = observer((props: any) => {
- const ctx = useContext(TableRowContext);
- const schema = props.schema;
- return (
-
-
-
- );
-});
-
-Table.Operation.DesignableBar = () => {
- const { t } = useTranslation();
- const { schema: columnSchema } = useDesignable();
- const groupSchema = Object.values(columnSchema.properties || {}).shift();
- const groupPath = getSchemaPath(groupSchema);
- const { schema, remove, refresh, appendChild } = useDesignable(groupPath);
- const [visible, setVisible] = useState(false);
- const { createSchema, removeSchema, updateSchema } = useClient();
-
- const map = new Map();
- schema.mapProperties((s) => {
- if (!s['x-action-type']) {
- return;
- }
- map.set(s['x-action-type'], s.name);
- });
- const path = getSchemaPath(schema);
- return (
-
- {
- e.stopPropagation();
- }}
- className={cls('designable-bar-actions', { active: visible })}
- >
-
-
- {
- setVisible(visible);
- }}
- overlay={
-
- }
- >
-
-
-
-
-
- );
-};
-
-Table.Action = () => null;
-
-Table.Action.DesignableBar = () => {
- const { t } = useTranslation();
- const { schema, remove, refresh, insertAfter } = useDesignable();
- const [visible, setVisible] = useState(false);
- const isPopup = Object.keys(schema.properties || {}).length > 0;
- const popupSchema = Object.values(schema.properties || {}).shift();
- const inActionBar = schema.parent['x-component'] === 'Table.ActionBar';
- const displayed = useDisplayedMapContext();
- const field = useField();
- const { createSchema, removeSchema, updateSchema } = useClient();
- const popupComponent = popupSchema?.['x-component'] || 'Action.Drawer';
- return (
-
- {
- e.stopPropagation();
- }}
- className={cls('designable-bar-actions', { active: visible })}
- >
-
-
- {
- setVisible(visible);
- }}
- overlay={
-
- }
- >
-
-
-
-
-
- );
-};
-
-Table.Cell = observer((props: any) => {
- const ctx = useContext(TableRowContext);
- const schema = props.schema;
- const collectionField = useContext(CollectionFieldContext);
- if (schema['x-component'] === 'Table.Operation') {
- return ;
- }
- let uiSchema = collectionField?.uiSchema as Schema;
- if (uiSchema?.['x-component'] === 'Upload.Attachment') {
- uiSchema = cloneDeepWith(uiSchema);
- set(uiSchema, 'x-component-props.size', 'small');
- }
- const componentProps = merge(
- uiSchema?.['x-component-props'] || {},
- schema?.['x-component-props'] || {},
- {
- arrayMerge: (t, s) => s,
- },
- );
- console.log('Table.Cell', collectionField?.interface, componentProps);
- return (
-
-
-
- );
-});
-
-Table.Column = observer((props: any) => {
- const collectionField = useContext(CollectionFieldContext);
- const { schema, DesignableBar } = useDesignable();
- const compile = useCompile();
- const displayed = useDisplayedMapContext();
- useEffect(() => {
- if (collectionField?.name) {
- displayed.set(collectionField.name, schema);
- }
- }, [collectionField, schema]);
- return (
-
- {compile(schema.title || collectionField?.uiSchema?.title)}
-
-
- );
-});
-
-Table.Column.DesignableBar = () => {
- const field = useField();
- const { t } = useTranslation();
- const compile = useCompile();
- const { service, refresh: refreshTable } = useTable();
- // const fieldSchema = useFieldSchema();
- const { schema, remove, refresh, insertAfter } = useDesignable();
- const [visible, setVisible] = useState(false);
- const displayed = useDisplayedMapContext();
- const { getFieldsByCollection } = useCollectionsContext();
- const collectionField = useContext(CollectionFieldContext);
- const { createSchema, removeSchema, updateSchema } = useClient();
- console.log('displayed.map', displayed.map);
- return (
-
-
{
- e.stopPropagation();
- }}
- className={cls('designable-bar-actions', { active: visible })}
- >
-
-
- {
- setVisible(visible);
- }}
- overlay={
-
- }
- >
-
-
-
-
-
- );
-};
-
-Table.Index = observer(() => {
- const index = useTableIndex();
- return {index + 1};
-});
-
-Table.SortHandle = observer((props: any) => {
- return ;
-});
-
-Table.DesignableBar = observer((props) => {
- const { t } = useTranslation();
- const compile = useCompile();
- const field = useField();
- const { schema, refresh, deepRemove } = useDesignable();
- const [visible, setVisible] = useState(false);
- const { dragRef } = useContext(DraggableBlockContext);
- const defaultPageSize =
- field?.componentProps?.pagination?.defaultPageSize || 10;
- const collectionName = field?.componentProps?.collectionName;
- const { collection, fields } = useCollection({ collectionName });
- const { createSchema, removeSchema, updateSchema } = useClient();
- console.log({ collectionName });
- return (
-
-
- {compile(collection?.title || collection?.name)}
-
-
{
- e.stopPropagation();
- }}
- className={cls('designable-bar-actions', { active: visible })}
- >
-
-
-
- {
- setVisible(visible);
- }}
- overlay={
-
- }
- >
-
-
-
-
-
- );
-});
-
-Table.useResource = ({ onSuccess, manual = true }) => {
- const { props } = useTable();
- const { collection } = useCollectionContext();
- const ctx = useContext(TableRowContext);
- const resource = useResourceRequest({
- resourceName: collection?.name || props.collectionName,
- resourceKey: ctx.record[props.rowKey],
- });
- const { schema } = useDesignable();
- const fieldFields = (schema: Schema) => {
- const names = [];
- schema.reduceProperties((buf, current) => {
- if (current['x-component'] === 'Form.Field') {
- const fieldName = current['x-component-props']?.['fieldName'];
- if (fieldName) {
- buf.push(fieldName);
- }
- } else {
- const fieldNames = fieldFields(current);
- buf.push(...fieldNames);
- }
- return buf;
- }, names);
- return names;
- };
- console.log(
- 'collection?.name || props.collectionName',
- collection?.name || props.collectionName,
- // fieldFields(schema),
- );
- const service = useRequest(
- (params?: any) => {
- console.log('Table.useResource', params);
- return resource.get({ ...params, appends: fieldFields(schema) });
- },
- {
- formatResult: (result) => result?.data,
- onSuccess,
- manual,
- },
- );
- return { resource, service, initialValues: service.data, ...service };
-};
-
-Table.useActionLogDetailsResource = ({ onSuccess }) => {
- const { props } = useTable();
- const { collection } = useCollectionContext();
- const ctx = useContext(TableRowContext);
- const resource = useResourceRequest({
- resourceName: 'action_logs',
- resourceKey: ctx.record[props.rowKey],
- });
- const service = useRequest(
- (params?: any) => {
- return resource.get({
- ...params,
- appends: ['changes', 'user', 'collection'],
- });
- },
- {
- formatResult: (result) => result?.data,
- onSuccess,
- manual: true,
- },
- );
- const [visible] = useContext(VisibleContext);
-
- useEffect(() => {
- if (visible) {
- service.run({});
- }
- }, [visible]);
-
- return { resource, service, initialValues: service.data, ...service };
-};
-
-const useActionLogsResource = (options: any = {}) => {
- const { props } = useTable();
- const ctx = useContext(TableRowContext);
-
- class ActionLogoResource extends Resource {
- list(options?: ListOptions) {
- console.log({ options });
- let defaultFilter = options?.defaultFilter;
- if (ctx?.record) {
- const extra = {
- index: ctx?.record?.id,
- collection_name: props.collectionName,
- };
- if (defaultFilter) {
- defaultFilter = { and: [defaultFilter, extra] };
- } else {
- defaultFilter = extra;
- }
- }
- return super.list({ ...options, defaultFilter });
- }
- }
-
- const resource = useResourceRequest('action_logs', ActionLogoResource);
-
- return {
- resource,
- };
-};
-
-Table.useActionLogsResource = useActionLogsResource;
-Table.useTableFilterAction = useTableFilterAction;
-Table.useTableCreateAction = useTableCreateAction;
-Table.useTableUpdateAction = useTableUpdateAction;
-Table.useTableDestroyAction = useTableDestroyAction;
-Table.useTableExportAction = useTableExportAction;
-Table.useTableIndex = useTableIndex;
-Table.useTableRowRecord = useTableRowRecord;
-Table.SimpleDesignableBar = SimpleDesignableBar;
diff --git a/packages/client/src/schemas/table/style.less b/packages/client/src/schemas/table/style.less
index e7fe0753f0..ec070bad70 100644
--- a/packages/client/src/schemas/table/style.less
+++ b/packages/client/src/schemas/table/style.less
@@ -186,9 +186,7 @@ td.nb-table-operation {
.ant-formily-item-feedback-layout-popover {
margin-bottom: 0;
}
- .ant-formily-item-control
- .ant-formily-item-control-content
- .ant-formily-item-control-content-component {
+ .ant-formily-item-control .ant-formily-item-control-content .ant-formily-item-control-content-component {
min-height: inherit;
line-height: inherit;
}
diff --git a/packages/client/src/schemas/table/types.ts b/packages/client/src/schemas/table/types.ts
new file mode 100644
index 0000000000..8ff51a61da
--- /dev/null
+++ b/packages/client/src/schemas/table/types.ts
@@ -0,0 +1,22 @@
+import { ArrayField } from '@formily/core';
+import { Schema } from '@formily/react';
+import { BaseResult } from '@ahooksjs/use-request/lib/types';
+import { Resource } from '../../resource';
+
+export interface ITableContext {
+ props: any;
+ field: ArrayField;
+ schema: Schema;
+ service: BaseResult;
+ selectedRowKeys?: any;
+ setSelectedRowKeys?: any;
+ pagination?: any;
+ setPagination?: any;
+ refresh?: any;
+ resource?: Resource;
+}
+
+export interface ITableRowContext {
+ index: number;
+ record: any;
+}
diff --git a/packages/client/src/schemas/table/utils.ts b/packages/client/src/schemas/table/utils.ts
new file mode 100644
index 0000000000..b59e8f10ed
--- /dev/null
+++ b/packages/client/src/schemas/table/utils.ts
@@ -0,0 +1,164 @@
+import { Schema } from '@formily/react';
+import { uid } from '@formily/shared';
+import { interfaces } from '../database-field/interfaces';
+import { ISchema } from '..';
+
+export const fieldsToSortColumns = (fields: any[]) => {
+ const dataSource = [];
+
+ fields.forEach((field) => {
+ const fieldOption = interfaces.get(field.interface);
+ if (!fieldOption?.sortable) {
+ return;
+ }
+ dataSource.push({
+ value: field.name,
+ label: field?.uiSchema?.title,
+ });
+ });
+
+ return dataSource;
+};
+
+export const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
+ const { fieldNames = [] } = options;
+ const properties = {};
+ fields.forEach((field, index) => {
+ if (fieldNames?.length && !fieldNames.includes(field.name)) {
+ return;
+ }
+ const fieldOption = interfaces.get(field.interface);
+ if (!fieldOption?.operations) {
+ return;
+ }
+ properties[`column${index}`] = {
+ type: 'void',
+ title: field?.uiSchema?.title,
+ 'x-component': 'Filter.Column',
+ 'x-component-props': {
+ operations: fieldOption.operations,
+ },
+ properties: {
+ [field.name]: {
+ ...field.uiSchema,
+ 'x-decorator': 'FormilyFormItem',
+ title: null,
+ },
+ },
+ };
+ });
+ return properties;
+};
+
+export const generateActionSchema = (type) => {
+ const actions: { [key: string]: ISchema } = {
+ filter: {
+ key: uid(),
+ name: uid(),
+ type: 'void',
+ title: "{{ t('Filter') }}",
+ 'x-align': 'left',
+ 'x-decorator': 'AddNew.Displayed',
+ 'x-decorator-props': {
+ displayName: 'filter',
+ },
+ 'x-component': 'Table.Filter',
+ 'x-designable-bar': 'Table.Filter.DesignableBar',
+ 'x-component-props': {
+ fieldNames: [],
+ },
+ },
+ export: {
+ key: uid(),
+ type: 'void',
+ name: uid(),
+ title: "{{ t('Export') }}",
+ 'x-align': 'right',
+ 'x-decorator': 'AddNew.Displayed',
+ 'x-decorator-props': {
+ displayName: 'export',
+ },
+ 'x-action-type': 'export',
+ 'x-component': 'Action',
+ 'x-designable-bar': 'Table.ExportActionDesignableBar',
+ 'x-component-props': {
+ fieldNames: [],
+ icon: 'ExportOutlined',
+ useAction: '{{ Table.useTableExportAction }}',
+ },
+ },
+ create: {
+ key: uid(),
+ type: 'void',
+ name: uid(),
+ title: "{{ t('Add new') }}",
+ 'x-align': 'right',
+ 'x-decorator': 'AddNew.Displayed',
+ 'x-decorator-props': {
+ displayName: 'create',
+ },
+ 'x-component': 'Action',
+ 'x-component-props': {
+ type: 'primary',
+ icon: 'PlusOutlined',
+ },
+ 'x-designable-bar': 'Table.Action.DesignableBar',
+ properties: {
+ modal: {
+ type: 'void',
+ title: "{{ t('Add record') }}",
+ 'x-decorator': 'Form',
+ 'x-component': 'Action.Drawer',
+ 'x-component-props': {
+ useOkAction: '{{ Table.useTableCreateAction }}',
+ },
+ properties: {
+ [uid()]: {
+ type: 'void',
+ 'x-component': 'Grid',
+ 'x-component-props': {
+ addNewComponent: 'AddNew.FormItem',
+ },
+ },
+ },
+ },
+ },
+ },
+ destroy: {
+ key: uid(),
+ type: 'void',
+ name: uid(),
+ title: "{{ t('Delete') }}",
+ 'x-align': 'right',
+ 'x-decorator': 'AddNew.Displayed',
+ 'x-decorator-props': {
+ displayName: 'destroy',
+ },
+ 'x-action-type': 'destroy',
+ 'x-component': 'Action',
+ 'x-designable-bar': 'Table.Action.DesignableBar',
+ 'x-component-props': {
+ confirm: {
+ title: "{{ t('Delete record') }}",
+ content: "{{ t('Are you sure you want to delete it?') }}",
+ },
+ useAction: '{{ Table.useTableDestroyAction }}',
+ },
+ },
+ view: {},
+ update: {},
+ };
+ return actions[type];
+};
+
+export function isColumn(schema: Schema) {
+ return ['Table.Column'].includes(schema['x-component']);
+}
+
+export function isColumnComponent(component: string) {
+ return ['Table.Operation', 'Table.Column'].includes(component);
+}
+
+export function isOperationColumn(schema: Schema) {
+ return ['Table.Operation'].includes(schema['x-component']);
+}