From e8399e167ea4557148a39c262ed340030c658d94 Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 20 Jul 2021 23:19:54 +0800 Subject: [PATCH] updates --- .../src/components/admin-layout/index.tsx | 14 +- .../src/components/schema-renderer/index.tsx | 16 +- packages/client/src/schemas/add-new/index.tsx | 558 +++++++++++++++++- .../client/src/schemas/block-item/index.tsx | 11 - .../client/src/schemas/card-item/index.tsx | 32 + .../src/schemas/database-field/index.tsx | 6 +- packages/client/src/schemas/form/index.tsx | 78 ++- .../client/src/schemas/grid/demos/demo1.tsx | 3 + packages/client/src/schemas/grid/index.tsx | 14 +- packages/client/src/schemas/index.tsx | 60 +- packages/client/src/schemas/markdown/index.md | 56 +- .../client/src/schemas/markdown/index.tsx | 108 +++- .../client/src/schemas/menu/defaultSchemas.ts | 114 ++-- packages/client/src/schemas/menu/index.tsx | 11 +- packages/client/src/schemas/page/index.tsx | 18 + packages/client/src/schemas/table/index.tsx | 58 +- .../plugin-ui-schema/src/models/ui-schema.ts | 3 +- 17 files changed, 1020 insertions(+), 140 deletions(-) create mode 100644 packages/client/src/schemas/card-item/index.tsx create mode 100644 packages/client/src/schemas/page/index.tsx diff --git a/packages/client/src/components/admin-layout/index.tsx b/packages/client/src/components/admin-layout/index.tsx index fa3efbf7a..df3f68b37 100644 --- a/packages/client/src/components/admin-layout/index.tsx +++ b/packages/client/src/components/admin-layout/index.tsx @@ -17,7 +17,7 @@ import { useHistory, Redirect, } from 'react-router-dom'; -import { SchemaRenderer } from '../../schemas'; +import { CollectionContextProvider, PageTitleContextProvider, SchemaRenderer, usePageTitleContext } from '../../schemas'; import { useRequest } from 'ahooks'; import './style.less'; @@ -30,6 +30,7 @@ function LayoutWithMenu({ schema }) { const location = useLocation(); const sideMenuRef = useRef(); const [activeKey, setActiveKey] = useState(match.params.name); + const [,setPageTitle] = usePageTitleContext(); const onSelect = (info) => { console.log('LayoutWithMenu', info); if (!info.schema) { @@ -39,6 +40,9 @@ function LayoutWithMenu({ schema }) { setActiveKey(null); } else { setActiveKey(info.schema.key); + if (info.schema.title) { + setPageTitle(info.schema.title); + } } }; console.log({ match }); @@ -98,7 +102,13 @@ export function AdminLayout({ route }: any) { return ; } - return ; + return ( + + + + + + ); } export default AdminLayout; diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx index 63371ee74..207ae7638 100644 --- a/packages/client/src/components/schema-renderer/index.tsx +++ b/packages/client/src/components/schema-renderer/index.tsx @@ -46,8 +46,10 @@ import { TimePicker } from '../../schemas/time-picker'; import { Upload } from '../../schemas/upload'; import { FormItem } from '../../schemas/form-item'; import { BlockItem } from '../../schemas/block-item'; +import { CardItem } from '../../schemas/card-item'; import { DragAndDrop } from '../../schemas/drag-and-drop'; import { TreeSelect } from '../../schemas/tree-select'; +import { Page } from '../../schemas/page'; export const BlockContext = createContext({ dragRef: null }); @@ -59,11 +61,14 @@ interface DesignableContextProps { } export const SchemaField = createSchemaField({ - scope: {}, + scope: { + Table, + }, components: { Card, Div, Space, + Page, ArrayCollapse, ArrayTable, @@ -73,6 +78,7 @@ export const SchemaField = createSchemaField({ DragAndDrop, BlockItem, + CardItem, FormItem, Action, @@ -173,6 +179,14 @@ function setKeys(schema: ISchema, parentKey = null) { }); } +export function useSchemaComponent(component: string) { + if (!component) { + return null; + } + const options = useContext(SchemaOptionsContext); + return get(options.components, component); +} + export function useDesignable(path?: any) { const { schema = new Schema({}), refresh } = useContext(DesignableContext); const schemaPath = path || useSchemaPath(); diff --git a/packages/client/src/schemas/add-new/index.tsx b/packages/client/src/schemas/add-new/index.tsx index d47e69bd0..bc3e2b663 100644 --- a/packages/client/src/schemas/add-new/index.tsx +++ b/packages/client/src/schemas/add-new/index.tsx @@ -46,9 +46,17 @@ import { DownOutlined, DatabaseOutlined, } from '@ant-design/icons'; -import { createSchema, useDesignable, useSchemaPath } from '../'; +import { + createOrUpdateCollection, + createSchema, + request, + useCollectionContext, + useDesignable, + useSchemaPath, +} from '../'; import { uid } from '@formily/shared'; -import { request } from '../../demos/api'; +import { useRequest } from 'ahooks'; +import { SchemaField } from '../../components/schema-renderer'; const generateGridBlock = (schema: ISchema) => { const name = schema.name || uid(); @@ -68,6 +76,10 @@ const generateGridBlock = (schema: ISchema) => { }; }; +const isGrid = (schema: Schema) => { + return schema['x-component'] === 'Grid'; +}; + const isGridBlock = (schema: Schema) => { if (schema.parent['x-component'] !== 'Grid.Col') { return false; @@ -83,14 +95,519 @@ const isGridBlock = (schema: Schema) => { return true; }; +function generateCardItemSchema(component) { + const defaults = { + Markdown: { + type: 'string', + default: '这是一段演示文字', + 'x-designable-bar': 'Markdown.DesignableBar', + 'x-decorator': 'CardItem', + 'x-read-pretty': true, + 'x-component': 'Markdown', + 'x-component-props': { + savedInSchema: true, + }, + }, + Table: { + type: 'array', + 'x-designable-bar': 'Table.DesignableBar', + 'x-decorator': 'CardItem', + 'x-component': 'Table', + default: [ + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + { key: uid(), field1: uid(), field2: uid() }, + ], + 'x-component-props': { + rowKey: 'key', + }, + properties: { + [`action_bar_${uid()}`]: { + type: 'void', + 'x-component': 'Table.ActionBar', + properties: { + action1: { + type: 'void', + name: 'action1', + title: '筛选', + 'x-component': 'Action', + properties: { + popover1: { + type: 'void', + title: '弹窗标题', + 'x-decorator': 'Form', + 'x-component': 'Action.Popover', + 'x-component-props': {}, + properties: { + filter: { + name: 'filter', + type: 'object', + 'x-component': 'Filter', + properties: { + column1: { + type: 'void', + title: '字段1', + 'x-component': 'Filter.Column', + 'x-component-props': { + operations: [ + { label: '等于', value: 'eq' }, + { label: '不等于', value: 'ne' }, + ], + }, + properties: { + field1: { + type: 'string', + 'x-component': 'Input', + }, + }, + }, + column2: { + type: 'void', + title: '字段2', + 'x-component': 'Filter.Column', + 'x-component-props': { + operations: [ + { label: '大于', value: 'gt' }, + { label: '小于', value: 'lt' }, + { + label: '非空', + value: 'notNull', + noValue: true, + }, + ], + }, + properties: { + field1: { + type: 'number', + 'x-component': 'InputNumber', + }, + }, + }, + }, + }, + }, + }, + }, + }, + action2: { + type: 'void', + name: 'action1', + title: '新增', + 'x-component': 'Action', + 'x-designable-bar': 'Action.DesignableBar', + properties: { + drawer1: { + type: 'void', + title: '抽屉标题', + 'x-decorator': 'Form', + 'x-component': 'Action.Modal', + 'x-component-props': { + useOkAction: '{{ Table.useTableCreateAction }}', + }, + properties: { + field1: { + type: 'string', + title: '字段1', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + field2: { + type: 'string', + title: '字段2', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + }, + }, + action3: { + type: 'void', + name: 'action1', + title: '删除', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ Table.useTableDestroyAction }}', + }, + }, + }, + }, + [`a_${uid()}`]: { + type: 'void', + 'x-component': 'Table.ActionBar', + 'x-component-props': { + align: 'bottom', + }, + properties: { + pagination: { + type: 'void', + 'x-component': 'Table.Pagination', + 'x-component-props': {}, + }, + }, + }, + [`column_${uid()}`]: { + type: 'void', + title: '排序', + 'x-component': 'Table.Column', + properties: { + sort: { + type: 'void', + 'x-component': 'Table.SortHandle', + }, + }, + }, + [`column_${uid()}`]: { + type: 'void', + title: '序号', + 'x-component': 'Table.Column', + properties: { + index: { + type: 'void', + 'x-component': 'Table.Index', + }, + }, + }, + [`column_${uid()}`]: { + type: 'void', + title: '字段1', + 'x-component': 'Table.Column', + 'x-component-props': { + // title: 'z1', + }, + 'x-designable-bar': 'Table.Column.DesignableBar', + properties: { + field1: { + type: 'string', + required: true, + 'x-read-pretty': true, + 'x-decorator-props': { + feedbackLayout: 'popover', + }, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + [`column_${uid()}`]: { + type: 'void', + title: '字段2', + 'x-component': 'Table.Column', + 'x-designable-bar': 'Table.Column.DesignableBar', + properties: { + field2: { + type: 'string', + // title: '字段2', + required: true, + 'x-read-pretty': true, + 'x-decorator-props': { + feedbackLayout: 'popover', + }, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + [`column_${uid()}`]: { + type: 'void', + title: '操作', + 'x-component': 'Table.Column', + properties: { + [uid()]: { + type: 'void', + name: 'action1', + title: '查看', + 'x-component': 'Action', + 'x-designable-bar': 'Table.Action.DesignableBar', + properties: { + drawer1: { + type: 'void', + title: '查看', + 'x-component': 'Action.Modal', + 'x-component-props': {}, + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Tabs', + properties: { + tab1: { + type: 'void', + 'x-component': 'Tabs.TabPane', + 'x-component-props': { + tab: 'Tab1', + }, + properties: { + aaa: { + type: 'string', + title: 'AAA', + 'x-decorator': 'FormItem', + required: true, + 'x-component': 'Input', + }, + }, + }, + tab2: { + type: 'void', + 'x-component': 'Tabs.TabPane', + 'x-component-props': { + tab: 'Tab2', + }, + properties: { + bbb: { + type: 'string', + title: 'BBB', + 'x-decorator': 'FormItem', + required: true, + 'x-component': 'Input', + }, + }, + }, + }, + }, + }, + }, + }, + }, + [uid()]: { + type: 'void', + name: 'action1', + title: '修改', + 'x-component': 'Action', + 'x-designable-bar': 'Table.Action.DesignableBar', + properties: { + drawer1: { + type: 'void', + title: '编辑表单', + 'x-decorator': 'Form', + 'x-decorator-props': { + useValues: '{{ Table.useTableRow }}', + }, + 'x-component': 'Action.Modal', + 'x-component-props': { + useOkAction: '{{ Table.useTableUpdateAction }}', + }, + properties: { + field1: { + type: 'string', + title: '字段1', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + field2: { + type: 'string', + title: '字段2', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + }, + }, + [uid()]: { + type: 'void', + title: '删除', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ Table.useTableDestroyAction }}', + }, + }, + }, + }, + }, + }, + Form: { + type: 'void', + name: uid(), + 'x-decorator': 'CardItem', + 'x-component': 'Form', + 'x-designable-bar': 'Form.DesignableBar', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Grid', + 'x-component-props': { + addNewComponent: 'AddNew.FormItem', + }, + }, + }, + }, + }; + return defaults[component]; +} + +function generateFormItemSchema(component) { + const defaults = { + Markdown: { + type: 'string', + title: uid(), + 'x-designable-bar': 'Markdown.DesignableBar', + 'x-decorator': 'FormItem', + 'x-component': 'Markdown', + 'x-component-props': {}, + }, + }; + return defaults[component]; +} + +const dbSchema = { + type: 'object', + properties: { + title: { + type: 'string', + title: '数据表名称', + required: true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + name: { + type: 'string', + title: '数据表标识', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + fields: { + type: 'array', + title: '数据表字段', + 'x-decorator': 'FormItem', + 'x-component': 'DatabaseField', + default: [], + }, + }, +}; + export const AddNew = () => null; +AddNew.CardItem = observer((props: any) => { + const { ghost, defaultAction } = props; + const { schema, insertBefore, insertAfter, appendChild } = useDesignable(); + const path = useSchemaPath(); + const { data: collections = [], loading, run } = useRequest('collections:findAll', { + formatResult: (result) => result?.data, + }); + console.log({ collections }); + return ( + { + if (visible) { + run(); + } + console.log('onVisibleChange', visible); + }} + placement={'bottomCenter'} + overlay={ + { + let data: ISchema; + if (['addNewTable', 'addNewForm'].includes(info.key)) { + const values = await FormDialog(`新建数据表`, () => { + return ( + + + + ); + }).open({ + initialValues: { + name: `t_${uid()}`, + fields: [], + }, + }); + await createOrUpdateCollection(values); + data = generateCardItemSchema( + info.key === 'addNewTable' ? 'Table' : 'Form', + ); + } else if (info.key !== 'Markdown') { + const keys = info.key.split('.'); + const component = keys.shift(); + const tableName = keys.join('.'); + data = generateCardItemSchema(component); + console.log('info.keyPath', component, tableName); + } else { + data = generateCardItemSchema(info.key); + } + if (schema['key']) { + data['key'] = uid(); + } + 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); + } + }} + > + + + {collections.map((item) => ( + {item.title} + ))} + + + 新建数据表 + + + + {collections.map((item) => ( + {item.title} + ))} + + + 新建数据表 + + 新建 Markdown + + + 模板1 + + + 新建模板 + + + } + > + {ghost ? ( + + ) : ( + + )} + + ); +}); + AddNew.BlockItem = observer((props: any) => { const { ghost, defaultAction } = props; const { schema, insertBefore, insertAfter } = useDesignable(); const path = useSchemaPath(); return ( @@ -113,7 +630,7 @@ AddNew.BlockItem = observer((props: any) => { type: 'void', title: uid(), 'x-designable-bar': 'BlockItem.DesignableBar', - 'x-decorator': 'BlockItem', + 'x-decorator': 'CardItem', 'x-component': 'Markdown', }; if (schema['key']) { @@ -141,7 +658,11 @@ AddNew.BlockItem = observer((props: any) => { } > - {ghost ? : } + {ghost ? ( + + ) : ( + + )} ); }); @@ -152,6 +673,10 @@ AddNew.FormItem = observer((props: any) => { const path = useSchemaPath(); return ( @@ -159,25 +684,24 @@ AddNew.FormItem = observer((props: any) => { style={{ minWidth: 150, }} - onClick={() => { - let data: ISchema = { - type: 'void', - title: uid(), - 'x-designable-bar': 'FormItem.DesignableBar', - 'x-decorator': 'FormItem', - 'x-component': 'Markdown', - }; + onClick={async () => { + let data: ISchema = generateFormItemSchema('Markdown'); + if (schema['key']) { + data['key'] = uid(); + } if (isGridBlock(schema)) { path.pop(); path.pop(); data = generateGridBlock(data); } if (data) { + let s; if (defaultAction === 'insertAfter') { - insertAfter(data, [...path]); + s = insertAfter(data, [...path]); } else { - insertBefore(data, [...path]); + s = insertBefore(data, [...path]); } + await createSchema(s); } }} > @@ -219,7 +743,11 @@ AddNew.FormItem = observer((props: any) => { } > - {ghost ? : } + {ghost ? ( + + ) : ( + + )} ); }); diff --git a/packages/client/src/schemas/block-item/index.tsx b/packages/client/src/schemas/block-item/index.tsx index e2e77f6e1..45b3394ca 100644 --- a/packages/client/src/schemas/block-item/index.tsx +++ b/packages/client/src/schemas/block-item/index.tsx @@ -113,17 +113,6 @@ export const BlockItem: any = observer((props) => { return React.createElement(uid ? DraggableBlock : Block, props); }); -// export const BlockItem: any = connect((props) => { -// const { children, ...others } = props; -// const { DesignableBar } = useDesignableBar(); -// return ( -//
-// {children} -// -//
-// ); -// }); - BlockItem.DesignableBar = () => { const field = useField(); const { schema, refresh } = useDesignable(); diff --git a/packages/client/src/schemas/card-item/index.tsx b/packages/client/src/schemas/card-item/index.tsx new file mode 100644 index 000000000..a0c3877ce --- /dev/null +++ b/packages/client/src/schemas/card-item/index.tsx @@ -0,0 +1,32 @@ +import React, { useContext, useState } from 'react'; +import { + connect, + mapProps, + mapReadPretty, + observer, + SchemaOptionsContext, + useField, + useFieldSchema, +} from '@formily/react'; +import { FormItem as FormilyFormItem } from '@formily/antd'; +import { Card, Dropdown, Menu, Space } from 'antd'; +import classNames from 'classnames'; +import { MenuOutlined, DragOutlined } from '@ant-design/icons'; +// import './style.less'; +import get from 'lodash/get'; +import { GridBlockContext } from '../grid'; +import { uid } from '@formily/shared'; +import { useDesignable } from '../'; +import { AddNew } from '../add-new'; +import { BlockItem } from '../block-item'; +import { DraggableBlockContext } from '../../components/drag-and-drop'; + +export const CardItem: any = connect((props) => { + return ( + + + {props.children} + + + ); +}); diff --git a/packages/client/src/schemas/database-field/index.tsx b/packages/client/src/schemas/database-field/index.tsx index d077c4e24..72636f041 100644 --- a/packages/client/src/schemas/database-field/index.tsx +++ b/packages/client/src/schemas/database-field/index.tsx @@ -39,7 +39,7 @@ import Modal from 'antd/lib/modal/Modal'; import { clone, cloneDeep, get } from 'lodash'; import { useEffect } from 'react'; import { useRequest } from 'ahooks'; -import { createOrUpdateCollection, deleteCollection } from '..'; +import { createOrUpdateCollection, deleteCollection, useCollectionContext } from '..'; export const DatabaseCollection = observer((props) => { const field = useField(); @@ -49,6 +49,7 @@ export const DatabaseCollection = observer((props) => { const [activeIndex, setActiveIndex] = useState(0); const form = useForm(); const [newValue, setNewValue] = useState(''); + const { refresh } = useCollectionContext(); const { run, loading } = useRequest('collections:findAll', { formatResult: (result) => result?.data, @@ -125,6 +126,7 @@ export const DatabaseCollection = observer((props) => { setOpen(false); setNewValue(''); await createOrUpdateCollection(data); + await refresh(); }} /> @@ -166,6 +168,7 @@ export const DatabaseCollection = observer((props) => { } if (item.name) { await deleteCollection(item.name); + await refresh(); } }} /> @@ -188,6 +191,7 @@ export const DatabaseCollection = observer((props) => { await form.validate(`${field.address.entire}.${activeIndex}.*`); delete field.value[activeIndex]['unsaved']; await createOrUpdateCollection(field.value[activeIndex]); + await refresh(); message.success('保存成功'); } catch (error) {} }} diff --git a/packages/client/src/schemas/form/index.tsx b/packages/client/src/schemas/form/index.tsx index 107415e0d..402bfb9bf 100644 --- a/packages/client/src/schemas/form/index.tsx +++ b/packages/client/src/schemas/form/index.tsx @@ -8,14 +8,18 @@ import { SchemaExpressionScopeContext, FormProvider, ISchema, + useField, } from '@formily/react'; -import { useSchemaPath, SchemaField, useDesignable } from '../'; +import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../'; import get from 'lodash/get'; -import { Dropdown, Menu } from 'antd'; -import { MenuOutlined } from '@ant-design/icons'; +import { Dropdown, Menu, Space } from 'antd'; +import { MenuOutlined, DragOutlined } from '@ant-design/icons'; import cls from 'classnames'; import { FormLayout } from '@formily/antd'; import './style.less'; +import AddNew from '../add-new'; +import { DraggableBlockContext } from '../../components/drag-and-drop'; +import { isGridRowOrCol } from '../grid'; export const Form: any = observer((props: any) => { const { useValues = () => ({}), ...others } = props; @@ -61,21 +65,59 @@ export const Form: any = observer((props: any) => { Form.__Decorator = ({ children }) => children; -Form.DesignableBar = (props) => { - const { active } = props; +Form.DesignableBar = observer((props) => { + const field = useField(); + const { schema, refresh, deepRemove } = useDesignable(); + const [visible, setVisible] = useState(false); + const { dragRef } = useContext(DraggableBlockContext); return ( -
-
- - 表单配置 - - } - > - - -
+
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + + {dragRef && } + { + setVisible(visible); + }} + overlay={ + + { + field.readPretty = false; + }} + > + 编辑Markdown + + + { + const removed = deepRemove(); + // console.log({ removed }) + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + }} + > + 删除当前区块 + + + } + > + + + +
); -}; +}); diff --git a/packages/client/src/schemas/grid/demos/demo1.tsx b/packages/client/src/schemas/grid/demos/demo1.tsx index ef1acab81..4de3895d5 100644 --- a/packages/client/src/schemas/grid/demos/demo1.tsx +++ b/packages/client/src/schemas/grid/demos/demo1.tsx @@ -14,6 +14,9 @@ const schema: ISchema = { type: 'void', name: uid(), 'x-component': 'Grid', + "x-component-props": { + addNewComponent: 'AddNew.CardItem', + }, properties: { [`row_${uid()}`]: { type: 'void', diff --git a/packages/client/src/schemas/grid/index.tsx b/packages/client/src/schemas/grid/index.tsx index c881de031..0bf320e8a 100644 --- a/packages/client/src/schemas/grid/index.tsx +++ b/packages/client/src/schemas/grid/index.tsx @@ -30,6 +30,7 @@ import { useDragDropUID, DragDropManagerProvider, } from '../../components/drag-and-drop'; +import { useSchemaComponent } from '../../components/schema-renderer'; const ColumnSizeContext = createContext(null); @@ -37,7 +38,7 @@ export const GridBlockContext = createContext({ dragRef: null, }); -function isGridRowOrCol(schema: ISchema) { +export function isGridRowOrCol(schema: ISchema) { return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']); } @@ -81,11 +82,14 @@ const ColDivider = (props: any) => { ); }; -export const Grid: any = observer((props) => { +export const Grid: any = observer((props: any) => { + const { addNewComponent } = props; + const AddNewComponent = useSchemaComponent(addNewComponent); const ref = useRef(); - const schema = useFieldSchema(); + // const schema = useFieldSchema(); const gridPath = useSchemaPath(); const { + schema, schema: designableSchema, insertAfter, prepend, @@ -162,6 +166,7 @@ export const Grid: any = observer((props) => { ); })} + {AddNewComponent && }
); @@ -169,9 +174,10 @@ export const Grid: any = observer((props) => { Grid.Row = observer((props) => { const field = useField(); - const schema = useFieldSchema(); + // const schema = useFieldSchema(); const rowPath = useSchemaPath(); const { + schema, schema: designableSchema, refresh, insertAfter, diff --git a/packages/client/src/schemas/index.tsx b/packages/client/src/schemas/index.tsx index f3a3ec3d9..8a4048967 100644 --- a/packages/client/src/schemas/index.tsx +++ b/packages/client/src/schemas/index.tsx @@ -1,18 +1,50 @@ -import { ISchema } from '@formily/react'; +import React from 'react'; +import { ISchema, observer, Schema, useField } from '@formily/react'; +import constate from 'constate'; +import { useState } from 'react'; import { createContext } from 'react'; +import { extend } from 'umi-request'; +import { isVoidField } from '@formily/core'; +import { useRequest } from 'ahooks'; + export * from '../components/schema-renderer'; + +export function mapReadPretty(component, readPrettyProps) { + return function (target) { + return observer( + (props) => { + const field = useField() + if (!isVoidField(field) && field?.pattern === 'readPretty') { + return React.createElement(component, { + ...readPrettyProps, + ...props, + }) + } + return React.createElement(target, props) + }, + { + forwardRef: true, + } + ) + }; +} + export const VisibleContext = createContext(null); export const DesignableBarContext = createContext(null); +const [PageTitleContextProvider, usePageTitleContext] = constate(() => { + return useState(null); +}); + +export { PageTitleContextProvider, usePageTitleContext }; + export function useDefaultAction() { return { - async run () {} - } + async run() {}, + }; } -import { extend } from 'umi-request'; - export const request = extend({ prefix: 'http://localhost:23003/api/', timeout: 1000, @@ -31,7 +63,7 @@ export async function deleteCollection(name) { params: { filter: { name, - } + }, }, }); } @@ -44,7 +76,7 @@ export async function createSchema(schema: ISchema) { method: 'post', data: schema.toJSON(), }); -}; +} export async function updateSchema(schema: ISchema) { if (!schema['key']) { @@ -52,9 +84,9 @@ export async function updateSchema(schema: ISchema) { } return await request(`ui_schemas:update/${schema.key}`, { method: 'post', - data: schema.toJSON(), + data: Schema.isSchemaInstance(schema) ? schema.toJSON() : schema, }); -}; +} export async function removeSchema(schema: ISchema) { if (!schema['key']) { @@ -65,7 +97,15 @@ export async function removeSchema(schema: ISchema) { params: { filter: { key: schema['key'], - } + }, }, }); } + +const [CollectionContextProvider, useCollectionContext] = constate(() => { + return useRequest('collections:findAll', { + formatResult: (result) => result?.data, + }); +}); + +export { CollectionContextProvider, useCollectionContext }; diff --git a/packages/client/src/schemas/markdown/index.md b/packages/client/src/schemas/markdown/index.md index e1272d22c..e5cc71d61 100644 --- a/packages/client/src/schemas/markdown/index.md +++ b/packages/client/src/schemas/markdown/index.md @@ -33,7 +33,6 @@ const schema = { type: 'object', properties: { input: { - interface: 'string', type: 'string', title: `编辑模式`, name: 'name1', @@ -76,3 +75,58 @@ export default () => { ); }; ``` + +### Markdown.DesignableBar + + +```tsx +import React from 'react'; +import { SchemaRenderer } from '../'; + +const schema1 = { + type: 'object', + properties: { + markdown1: { + type: 'string', + title: `简易Markdown`, + default: '这是一段示例文案', + 'x-read-pretty': true, + 'x-decorator': 'CardItem', + 'x-component': 'Markdown', + 'x-designable-bar': 'Markdown.DesignableBar', + 'x-component-props': { + savedInSchema: true, + // ellipsis: true, + }, + }, + }, +}; + +const schema2 = { + type: 'object', + properties: { + markdown1: { + type: 'string', + title: `简易Markdown`, + default: '这是一段示例文案', + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Markdown', + 'x-designable-bar': 'Markdown.DesignableBar', + 'x-component-props': { + }, + }, + }, +}; + +export default () => { + return ( +
+

区块项

+ +

表单项

+ +
+ ); +}; +``` \ No newline at end of file diff --git a/packages/client/src/schemas/markdown/index.tsx b/packages/client/src/schemas/markdown/index.tsx index 797985fdb..270fbb4e8 100644 --- a/packages/client/src/schemas/markdown/index.tsx +++ b/packages/client/src/schemas/markdown/index.tsx @@ -1,15 +1,53 @@ import React from 'react'; -import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react'; -import { Input as AntdInput } from 'antd'; +import { + connect, + mapProps, + mapReadPretty, + observer, + useField, + useFieldSchema, +} from '@formily/react'; +import { Button, Dropdown, Input as AntdInput, Menu, Space } from 'antd'; import { InputProps, TextAreaProps } from 'antd/lib/input'; import { Display } from '../display'; -import { LoadingOutlined } from '@ant-design/icons'; +import { LoadingOutlined, MenuOutlined, DragOutlined } from '@ant-design/icons'; import micromark from 'micromark'; +import { useDesignable } from '../../components/schema-renderer'; +import { useContext, useState } from 'react'; +import AddNew from '../add-new'; +import cls from 'classnames'; +import { DraggableBlockContext } from '../../components/drag-and-drop'; +import { uid } from '@formily/shared'; +import { removeSchema, updateSchema } from '..'; +import { isGridRowOrCol } from '../grid'; -export const Markdown = connect( +export const Markdown: any = connect( (props) => { - const schema = useFieldSchema(); - return + const { schema } = useDesignable(); + const field = useField(); + const { savedInSchema, ...others } = props; + console.log('Markdown', props); + return savedInSchema ? ( +
+ + +
+ ) : ( + + ); }, mapProps((props: any, field) => { return { @@ -26,6 +64,7 @@ export const Markdown = connect( }; }), mapReadPretty((props) => { + console.log('Markdown', props); let text = props.value; let value = (
@@ -34,4 +73,61 @@ export const Markdown = connect( }), ); +Markdown.DesignableBar = observer((props) => { + const field = useField(); + const { schema, refresh, deepRemove } = useDesignable(); + const [visible, setVisible] = useState(false); + const { dragRef } = useContext(DraggableBlockContext); + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + + {dragRef && } + { + setVisible(visible); + }} + overlay={ + + { + field.readPretty = false; + }} + > + 修改 Markdown + + + { + const removed = deepRemove(); + // console.log({ removed }) + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + }} + > + 删除当前区块 + + + } + > + + + + +
+ ); +}); + export default Markdown; diff --git a/packages/client/src/schemas/menu/defaultSchemas.ts b/packages/client/src/schemas/menu/defaultSchemas.ts index dc9fa6e30..1a0bffce3 100644 --- a/packages/client/src/schemas/menu/defaultSchemas.ts +++ b/packages/client/src/schemas/menu/defaultSchemas.ts @@ -1,71 +1,59 @@ import { ISchema } from '@formily/react'; import { uid } from '@formily/shared'; -export const defaultSchemas = { - MixMenu: { - type: 'void', - 'x-component': 'Menu', - 'x-designable-bar': 'Menu.DesignableBar', - 'x-component-props': { - sideMenuRef: '{{ sideMenuRef }}', - mode: 'mix', - theme: 'dark', - // onSelect: '{{ onSelect }}', +export function generateDefaultSchema(component) { + const defaultSchemas: { [key: string]: ISchema } = { + MixMenu: { + type: 'void', + 'x-component': 'Menu', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component-props': { + sideMenuRef: '{{ sideMenuRef }}', + mode: 'mix', + theme: 'dark', + // onSelect: '{{ onSelect }}', + }, }, - }, - 'Menu.Item': { - type: 'void', - 'x-designable-bar': 'Menu.DesignableBar', - 'x-component': 'Menu.Item', - }, - 'Menu.Link': { - type: 'void', - 'x-designable-bar': 'Menu.DesignableBar', - 'x-component': 'Menu.Link', - properties: { - [uid()]: { - type: 'void', - name: uid(), - 'x-component': 'Grid', - properties: { - [`row_${uid()}`]: { - type: 'void', - 'x-component': 'Grid.Row', - 'x-component-props': { - locked: true, - }, - properties: { - [`col_${uid()}`]: { - type: 'void', - 'x-component': 'Grid.Col', - properties: { - [uid()]: { - type: 'void', - 'x-component': 'AddNew.BlockItem', - }, - }, + 'Menu.Item': { + type: 'void', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component': 'Menu.Item', + }, + 'Menu.Link': { + type: 'void', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component': 'Menu.Link', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Page', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Grid', + 'x-component-props': { + addNewComponent: 'AddNew.CardItem', }, }, }, }, - } - } - }, - 'Menu.URL': { - type: 'void', - 'x-designable-bar': 'Menu.DesignableBar', - 'x-component': 'Menu.URL', - }, - 'Menu.SubMenu': { - type: 'void', - 'x-designable-bar': 'Menu.DesignableBar', - 'x-component': 'Menu.SubMenu', - }, - 'Menu.Action': { - type: 'void', - 'x-designable-bar': 'Menu.DesignableBar', - 'x-component': 'Menu.Action', - }, -}; - -export default defaultSchemas; + }, + }, + 'Menu.URL': { + type: 'void', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component': 'Menu.URL', + }, + 'Menu.SubMenu': { + type: 'void', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component': 'Menu.SubMenu', + }, + 'Menu.Action': { + type: 'void', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component': 'Menu.Action', + }, + }; + return defaultSchemas[component]; +} diff --git a/packages/client/src/schemas/menu/index.tsx b/packages/client/src/schemas/menu/index.tsx index ec288b98a..d383f0c54 100644 --- a/packages/client/src/schemas/menu/index.tsx +++ b/packages/client/src/schemas/menu/index.tsx @@ -62,7 +62,7 @@ import './style.less'; import { Link } from 'react-router-dom'; import { findPropertyByPath, getSchemaPath, useSchemaPath } from '../../'; import { request } from '../'; -import defaultSchemas from './defaultSchemas'; +import { generateDefaultSchema } from './defaultSchemas'; import _, { cloneDeep, get, isNull } from 'lodash'; import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd'; import deepmerge from 'deepmerge'; @@ -369,7 +369,7 @@ Menu.AddNew = observer((props: any) => { ); }).open(); - const defaults = cloneDeep(defaultSchemas[info.key]); + const defaults = generateDefaultSchema(info.key); const data = appendChild(deepmerge(defaults, values)); await createSchema(data); }} @@ -568,7 +568,7 @@ Menu.DesignableBar = (props) => { insertBefore, appendChild, }; - const defaults = cloneDeep(defaultSchemas[type]); + const defaults = generateDefaultSchema(type); const data = methods[method](deepmerge(defaults, values)); await createSchema(data); }} @@ -711,11 +711,12 @@ Menu.DesignableBar = (props) => { insertBefore, appendChild, }; + const data = schema.toJSON(); + remove(); const source = methods[values.method]( - schema.toJSON(), + data, values.path, ); - remove(); await updateSchema(source); }} > diff --git a/packages/client/src/schemas/page/index.tsx b/packages/client/src/schemas/page/index.tsx new file mode 100644 index 000000000..caa109575 --- /dev/null +++ b/packages/client/src/schemas/page/index.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { PageHeader as AntdPageHeader } from 'antd'; +import { observer } from '@formily/react'; +import { usePageTitleContext } from '..'; + +export const Page = observer((props) => { + const { children, ...others } = props; + const [pageTitle] = usePageTitleContext(); + + return ( + <> + +
+ {children} +
+ + ); +}); diff --git a/packages/client/src/schemas/table/index.tsx b/packages/client/src/schemas/table/index.tsx index ec34214e9..2c8cd59e3 100644 --- a/packages/client/src/schemas/table/index.tsx +++ b/packages/client/src/schemas/table/index.tsx @@ -25,7 +25,7 @@ import constate from 'constate'; import useRequest from '@ahooksjs/use-request'; import { BaseResult } from '@ahooksjs/use-request/lib/types'; import { uid, clone } from '@formily/shared'; -import { MenuOutlined } from '@ant-design/icons'; +import { MenuOutlined, DragOutlined } from '@ant-design/icons'; import { SortableHandle, SortableContainer, @@ -34,6 +34,8 @@ import { import cls from 'classnames'; import { getSchemaPath, useDesignable, useSchemaPath, VisibleContext } from '../'; import './style.less'; +import { DraggableBlockContext } from '../../components/drag-and-drop'; +import AddNew from '../add-new'; interface TableRowProps { index: number; @@ -632,4 +634,56 @@ Table.Action.DesignableBar = () => {
); -}; \ No newline at end of file +}; + +Table.DesignableBar = observer((props) => { + const field = useField(); + const { schema, refresh, deepRemove } = useDesignable(); + const [visible, setVisible] = useState(false); + const { dragRef } = useContext(DraggableBlockContext); + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + + {dragRef && } + { + setVisible(visible); + }} + overlay={ + + { + field.readPretty = false; + }} + > + 编辑Markdown + + + { + deepRemove(); + }} + > + 删除当前区块 + + + } + > + + + + +
+ ); +}); diff --git a/packages/plugin-ui-schema/src/models/ui-schema.ts b/packages/plugin-ui-schema/src/models/ui-schema.ts index 1352d3b15..2f1544f80 100644 --- a/packages/plugin-ui-schema/src/models/ui-schema.ts +++ b/packages/plugin-ui-schema/src/models/ui-schema.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import { Model } from '@nocobase/database'; +import deepmerge from 'deepmerge'; export class UISchema extends Model { static async create(value?: any, options?: any): Promise { @@ -33,7 +34,7 @@ export class UISchema extends Model { ]; const attrs = _.pick(data, keys); const options = _.omit(data, keys); - return { ...attrs, options: {...options, ...opts} }; + return { ...attrs, options: deepmerge(opts, options) }; } static properties2children(properties = []) {