From c3d2c3cd28bb14d5c74f13a6d33b015e203aef27 Mon Sep 17 00:00:00 2001 From: chenos Date: Fri, 30 Jul 2021 16:10:40 +0800 Subject: [PATCH] action bar --- .../client/src/components/SwitchMenuItem.tsx | 40 ++ packages/client/src/schemas/add-new/index.tsx | 149 +++--- .../database-field/interfaces/linkTo.ts | 4 - packages/client/src/schemas/table/index.tsx | 479 ++++++++++++++---- packages/client/src/schemas/table/style.less | 1 + 5 files changed, 502 insertions(+), 171 deletions(-) create mode 100644 packages/client/src/components/SwitchMenuItem.tsx diff --git a/packages/client/src/components/SwitchMenuItem.tsx b/packages/client/src/components/SwitchMenuItem.tsx new file mode 100644 index 000000000..60707fcea --- /dev/null +++ b/packages/client/src/components/SwitchMenuItem.tsx @@ -0,0 +1,40 @@ +import React, { useEffect, useState } from 'react'; +import { Menu, Switch } from 'antd'; + +export interface SwitchMenuItemProps { + onChange?: any; + title?: any; + checked?: boolean; +} + +export function SwitchMenuItem(props: SwitchMenuItemProps) { + const { onChange } = props; + const [checked, setChecked] = useState(props.checked); + useEffect(() => { + setChecked(props.checked); + }, [props.checked]); + return ( + { + setChecked((checked) => { + onChange(!checked); + return !checked; + }); + }} + > +
+ {props.title} + +
+
+ ); +} + +export default SwitchMenuItem; diff --git a/packages/client/src/schemas/add-new/index.tsx b/packages/client/src/schemas/add-new/index.tsx index 69f3a8e86..afb7bc271 100644 --- a/packages/client/src/schemas/add-new/index.tsx +++ b/packages/client/src/schemas/add-new/index.tsx @@ -69,6 +69,7 @@ import { useCollectionsContext, useDisplayedMapContext, } from '../../constate'; +import SwitchMenuItem from '../../components/SwitchMenuItem'; const generateGridBlock = (schema: ISchema) => { const name = schema.name || uid(); @@ -149,6 +150,12 @@ function generateCardItemSchema(component) { properties: { [uid()]: { type: 'void', + title: '筛选', + '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': { @@ -159,6 +166,11 @@ function generateCardItemSchema(component) { type: 'void', name: 'action1', title: '新增', + 'x-align': 'right', + 'x-decorator': 'AddNew.Displayed', + 'x-decorator-props': { + displayName: 'create', + }, 'x-component': 'Action', 'x-designable-bar': 'Table.Action.DesignableBar', properties: { @@ -186,6 +198,11 @@ function generateCardItemSchema(component) { type: 'void', name: 'action1', title: '删除', + 'x-align': 'right', + 'x-decorator': 'AddNew.Displayed', + 'x-decorator-props': { + displayName: 'destroy', + }, 'x-component': 'Action', 'x-designable-bar': 'Table.Action.DesignableBar', 'x-component-props': { @@ -201,6 +218,7 @@ function generateCardItemSchema(component) { 'x-component-props': { className: 'nb-table-operation', }, + 'x-designable-bar': 'Table.OperationDesignableBar', properties: { [uid()]: { type: 'void', @@ -578,32 +596,6 @@ AddNew.CardItem = observer((props: any) => { ); }); -function SwitchField(props) { - const { field, onChange } = props; - const [checked, setChecked] = useState(props.checked); - useEffect(() => { - setChecked(props.checked); - }, [props.checked]); - return ( -
{ - setChecked((checked) => { - onChange(!checked); - return !checked; - }); - }} - > - {field.uiSchema.title} - -
- ); -} - AddNew.FormItem = observer((props: any) => { const { ghost, defaultAction } = props; const { schema, insertBefore, insertAfter, appendChild, deepRemove } = @@ -624,57 +616,56 @@ AddNew.FormItem = observer((props: any) => { {fields?.map((field) => ( - - { - if (!checked) { - const s: any = displayed.get(field.name); - const p = getSchemaPath(s); - const removed = deepRemove(p); - if (!removed) { - console.log('getSchemaPath', p, removed); - return; - } - const last = removed.pop(); - displayed.remove(field.name); - if (isGridRowOrCol(last)) { - await removeSchema(last); - } + { + if (!checked) { + const s: any = displayed.get(field.name); + const p = getSchemaPath(s); + const removed = deepRemove(p); + if (!removed) { + console.log('getSchemaPath', p, removed); return; } - let data: ISchema = { - key: uid(), - type: 'void', - 'x-decorator': 'Form.Field.Item', - 'x-designable-bar': 'Form.Field.DesignableBar', - 'x-component': 'Form.Field', - 'x-component-props': { - fieldName: field.name, - }, - }; - if (isGridBlock(schema)) { - path.pop(); - path.pop(); - data = generateGridBlock(data); - } else if (isGrid(schema)) { - data = generateGridBlock(data); + const last = removed.pop(); + displayed.remove(field.name); + if (isGridRowOrCol(last)) { + await removeSchema(last); } - if (data) { - let s; - if (isGrid(schema)) { - s = appendChild(data, [...path]); - } else if (defaultAction === 'insertAfter') { - s = insertAfter(data, [...path]); - } else { - s = insertBefore(data, [...path]); - } - await createSchema(s); + return; + } + let data: ISchema = { + key: uid(), + type: 'void', + 'x-decorator': 'Form.Field.Item', + 'x-designable-bar': 'Form.Field.DesignableBar', + 'x-component': 'Form.Field', + 'x-component-props': { + fieldName: field.name, + }, + }; + if (isGridBlock(schema)) { + path.pop(); + path.pop(); + data = generateGridBlock(data); + } else if (isGrid(schema)) { + data = generateGridBlock(data); + } + if (data) { + let s; + if (isGrid(schema)) { + s = appendChild(data, [...path]); + } else if (defaultAction === 'insertAfter') { + s = insertAfter(data, [...path]); + } else { + s = insertBefore(data, [...path]); } - }} - /> - + await createSchema(s); + } + }} + /> ))} @@ -968,4 +959,16 @@ AddNew.PaneItem = observer((props: any) => { ); }); +AddNew.Displayed = observer((props: any) => { + const { displayName, children } = props; + const displayed = useDisplayedMapContext(); + const { schema } = useDesignable(); + useEffect(() => { + if (displayName) { + displayed.set(displayName, schema); + } + }, [displayName, schema]); + return children; +}); + export default AddNew; diff --git a/packages/client/src/schemas/database-field/interfaces/linkTo.ts b/packages/client/src/schemas/database-field/interfaces/linkTo.ts index b415c4f2a..e80fa46f7 100644 --- a/packages/client/src/schemas/database-field/interfaces/linkTo.ts +++ b/packages/client/src/schemas/database-field/interfaces/linkTo.ts @@ -56,8 +56,4 @@ export const linkTo: ISchema = { 'x-component': 'Checkbox', }, }, - operations: [ - { label: '等于', value: 'eq' }, - { label: '不等于', value: 'ne' }, - ], }; diff --git a/packages/client/src/schemas/table/index.tsx b/packages/client/src/schemas/table/index.tsx index 710215eea..d438ecc8d 100644 --- a/packages/client/src/schemas/table/index.tsx +++ b/packages/client/src/schemas/table/index.tsx @@ -1,11 +1,13 @@ import { + FormProvider, + ISchema, observer, RecursionField, Schema, useField, useForm, } from '@formily/react'; -import { Pagination, Table as AntdTable } from 'antd'; +import { Pagination, Popover, Table as AntdTable } from 'antd'; import { findIndex, forIn, range, set } from 'lodash'; import React, { Fragment, useEffect, useState } from 'react'; import { useContext } from 'react'; @@ -22,8 +24,12 @@ import { CSS } from '@dnd-kit/utilities'; import { Select, Dropdown, Menu, Switch, Button, Space } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; import './style.less'; -import { getSchemaPath } from '../../components/schema-renderer'; -import { options } from '../database-field/interfaces'; +import { + getSchemaPath, + SchemaField, + SchemaRenderer, +} from '../../components/schema-renderer'; +import { interfaces, options } from '../database-field/interfaces'; import { DraggableBlockContext } from '../../components/drag-and-drop'; import AddNew from '../add-new'; import { isGridRowOrCol } from '../grid'; @@ -35,6 +41,9 @@ import { useDisplayedMapContext, } from '../../constate'; import { useResource as useGeneralResource } from '../../hooks/useResource'; +import SwitchMenuItem from '../../components/SwitchMenuItem'; +import { useMemo } from 'react'; +import { createForm } from '@formily/core'; const SyntheticListenerMapContext = createContext(null); @@ -247,6 +256,7 @@ const useTableColumns = () => { schema, props: { rowKey }, } = useTable(); + const { designable } = useDesignable(); const { getField } = useCollectionContext(); @@ -284,40 +294,18 @@ const useTableColumns = () => { }, }; }) - .concat([ - { - title: , - dataIndex: 'addnew', - }, - ]); + .concat( + designable + ? [ + { + title: , + dataIndex: 'addnew', + }, + ] + : [], + ); }; -function SwitchField(props) { - const { field, onChange } = props; - const [checked, setChecked] = useState(props.checked); - useEffect(() => { - setChecked(props.checked); - }, [props.checked]); - return ( -
{ - setChecked((checked) => { - onChange(!checked); - return !checked; - }); - }} - > - {field.uiSchema.title} - -
- ); -} - function AddColumn() { const [visible, setVisible] = useState(false); const { appendChild, remove } = useDesignable(); @@ -333,31 +321,29 @@ function AddColumn() { {fields.map((field) => ( - - { - if (checked) { - const data = appendChild({ - type: 'void', - 'x-component': 'Table.Column', - 'x-component-props': { - fieldName: field.name, - }, - 'x-designable-bar': 'Table.Column.DesignableBar', - }); - await createSchema(data); - } else { - const s: any = displayed.get(field.name); - const p = getSchemaPath(s); - const removed = remove(p); - await removeSchema(removed); - displayed.remove(field.name); - } - }} - /> - + { + if (checked) { + const data = appendChild({ + type: 'void', + 'x-component': 'Table.Column', + 'x-component-props': { + fieldName: field.name, + }, + 'x-designable-bar': 'Table.Column.DesignableBar', + }); + await createSchema(data); + } else { + const s: any = displayed.get(field.name); + const p = getSchemaPath(s); + const removed = remove(p); + await removeSchema(removed); + displayed.remove(field.name); + } + }} + /> ))} @@ -382,7 +368,9 @@ function AddColumn() { } > - + ); } @@ -582,7 +570,7 @@ const useTotal = () => { }; Table.Pagination = observer(() => { - const { field, service, pagination, setPagination, props } = useTable(); + const { service, pagination, setPagination, props } = useTable(); if (!pagination || Object.keys(pagination).length === 0) { return null; } @@ -590,7 +578,7 @@ Table.Pagination = observer(() => { const total = useTotal(); const { page = 1 } = pagination; return ( -
+
{ ); }); +function generateActionSchema(type) { + const actions: { [key: string]: ISchema } = { + filter: { + key: uid(), + name: uid(), + type: 'void', + title: '筛选', + '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: {}, + create: { + key: uid(), + type: 'void', + name: uid(), + title: '新增', + 'x-align': 'right', + 'x-decorator': 'AddNew.Displayed', + 'x-decorator-props': { + displayName: 'create', + }, + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + }, + 'x-designable-bar': 'Table.Action.DesignableBar', + properties: { + modal: { + type: 'void', + title: '新增数据', + 'x-decorator': 'Form', + 'x-component': 'Action.Modal', + '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: '删除', + 'x-align': 'right', + 'x-decorator': 'AddNew.Displayed', + 'x-decorator-props': { + displayName: 'destroy', + }, + 'x-component': 'Action', + 'x-designable-bar': 'Table.Action.DesignableBar', + 'x-component-props': { + useAction: '{{ Table.useTableDestroyAction }}', + }, + }, + update: {}, + }; + return actions[type]; +} + function AddActionButton() { const [visible, setVisible] = useState(false); + const displayed = useDisplayedMapContext(); + const { appendChild, remove } = useDesignable(); + const { schema, designable } = useDesignable(); + if (!designable) { + return null; + } return ( - + {[ - { title: '筛选', key: 'filter' }, - { title: '导出', key: 'export' }, - { title: '新增', key: 'create' }, - { title: '删除', key: 'destroy' }, + { title: '筛选', name: 'filter' }, + // { title: '导出', name: 'export' }, + { title: '新增', name: 'create' }, + { title: '删除', name: 'destroy' }, ].map((item) => ( - -
- {item.title} - -
-
+ { + 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); + } + }} + /> ))}
@@ -656,28 +732,233 @@ function AddActionButton() {
} > - ); } +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 designableBar = schema['x-designable-bar']; - console.log('Table.ActionBar', schema); + console.log('Table.ActionBar', { schema }); return ( -
- - {props.children} - {designable && designableBar && } - -
+ +
+
+ +
+
+ +
+ +
+
); }); +Table.Filter = observer((props: any) => { + const { fieldNames = [] } = props; + const { schema, DesignableBar } = useDesignable(); + const form = useMemo(() => createForm(), []); + const { fields = [] } = useCollectionContext(); + const fields2properties = (fields: any[]) => { + 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, + title: null, + }, + }, + }; + }); + return properties; + }; + return ( + + + + + + } + > + + + ); +}); + +Table.Filter.DesignableBar = () => { + const { schema, remove, refresh, insertAfter } = useDesignable(); + const [visible, setVisible] = useState(false); + const displayed = useDisplayedMapContext(); + const { fields } = useCollectionContext(); + + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + { + setVisible(visible); + }} + overlay={ + + + {fields + .filter((field) => { + const option = interfaces.get(field.interface); + return option.operations?.length; + }) + .map((field) => ( + {}} + /> + ))} + + + { + schema.title = uid(); + refresh(); + }} + > + 修改名称和图标 + + + { + const displayName = + schema?.['x-decorator-props']?.['displayName']; + const data = remove(); + await removeSchema(data); + if (displayName) { + displayed.remove(displayName); + } + setVisible(false); + }} + > + 删除 + + + } + > + + + +
+ ); +}; + +Table.OperationDesignableBar = () => { + const { schema, remove, refresh, insertAfter } = useDesignable(); + const [visible, setVisible] = useState(false); + const isPopup = Object.keys(schema.properties || {}).length > 0; + const inActionBar = schema.parent['x-component'] === 'Table.ActionBar'; + + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + { + setVisible(visible); + }} + overlay={ + + + {[ + { title: '查看', name: 'view' }, + { title: '编辑', name: 'update' }, + { title: '删除', name: 'destroy' }, + ].map((item) => ( + {}} + /> + ))} + + + + 函数操作 + 弹窗表单 + 复杂弹窗 + + + } + > + + + +
+ ); +}; + Table.Action = () => null; Table.Action.DesignableBar = () => { @@ -685,7 +966,7 @@ Table.Action.DesignableBar = () => { const [visible, setVisible] = useState(false); const isPopup = Object.keys(schema.properties || {}).length > 0; const inActionBar = schema.parent['x-component'] === 'Table.ActionBar'; - + const displayed = useDisplayedMapContext(); return (
{ 内打开 )} - {inActionBar ? ( - 放置在右侧 - ) : ( + {!inActionBar && ( 点击表格行时触发    )} - - 删除 + { + const displayName = + schema?.['x-decorator-props']?.['displayName']; + const data = remove(); + await removeSchema(data); + if (displayName) { + displayed.remove(displayName); + } + setVisible(false); + }} + > + 删除 + } > diff --git a/packages/client/src/schemas/table/style.less b/packages/client/src/schemas/table/style.less index 48987094c..b9dd3409f 100644 --- a/packages/client/src/schemas/table/style.less +++ b/packages/client/src/schemas/table/style.less @@ -67,6 +67,7 @@ &.align-bottom { margin-top: 8px; } + display: flex; } th.nb-table-operation,