From 8d298de6fe5166131a2fcd9e6c9b0a7b7e5e0ed3 Mon Sep 17 00:00:00 2001 From: chenos Date: Wed, 21 Jul 2021 21:51:16 +0800 Subject: [PATCH] updates --- .../src/components/admin-layout/index.tsx | 43 +++- .../src/components/admin-layout/style.less | 7 + .../src/components/schema-renderer/index.tsx | 28 ++- packages/client/src/demos/demo1.tsx | 2 + packages/client/src/schemas/action/index.tsx | 2 +- packages/client/src/schemas/add-new/index.tsx | 215 ++++++++++++------ .../client/src/schemas/block-item/index.tsx | 6 +- .../database-field/interfaces/select.ts | 3 + .../database-field/interfaces/string.ts | 3 + .../database-field/interfaces/subTable.ts | 5 +- .../database-field/interfaces/textarea.ts | 2 + .../client/src/schemas/form/DesignableBar.tsx | 94 ++++++++ .../src/schemas/form/Field.DesignableBar.tsx | 114 ++++++++++ packages/client/src/schemas/form/index.md | 7 + packages/client/src/schemas/form/index.tsx | 201 +++++++++------- packages/client/src/schemas/grid/index.tsx | 5 +- packages/client/src/schemas/index.tsx | 32 ++- packages/client/src/schemas/input/index.tsx | 73 +++++- .../client/src/schemas/markdown/index.tsx | 70 +++++- .../client/src/schemas/markdown/style.less | 3 + packages/client/src/schemas/menu/index.tsx | 14 +- packages/client/src/schemas/table/index.tsx | 23 +- 22 files changed, 767 insertions(+), 185 deletions(-) create mode 100644 packages/client/src/schemas/form/DesignableBar.tsx create mode 100644 packages/client/src/schemas/form/Field.DesignableBar.tsx create mode 100644 packages/client/src/schemas/markdown/style.less diff --git a/packages/client/src/components/admin-layout/index.tsx b/packages/client/src/components/admin-layout/index.tsx index df3f68b37..693142055 100644 --- a/packages/client/src/components/admin-layout/index.tsx +++ b/packages/client/src/components/admin-layout/index.tsx @@ -17,20 +17,46 @@ import { useHistory, Redirect, } from 'react-router-dom'; -import { CollectionContextProvider, PageTitleContextProvider, SchemaRenderer, usePageTitleContext } from '../../schemas'; +import { + CollectionContextProvider, + PageTitleContextProvider, + SchemaRenderer, + SwithDesignableContextProvider, + usePageTitleContext, + useSwithDesignableContext, +} from '../../schemas'; import { useRequest } from 'ahooks'; import './style.less'; import { uid } from '@formily/shared'; import { ISchema } from '@formily/react'; import Database from './datatable'; +import { HighlightOutlined } from '@ant-design/icons'; +import { useCookieState } from 'ahooks'; +import cls from 'classnames'; + +function DesignableToggle() { + const { designable, setDesignable } = useSwithDesignableContext(); + return ( + + ); +} function LayoutWithMenu({ schema }) { const match = useRouteMatch(); const location = useLocation(); const sideMenuRef = useRef(); const [activeKey, setActiveKey] = useState(match.params.name); - const [,setPageTitle] = usePageTitleContext(); + const [, setPageTitle] = usePageTitleContext(); const onSelect = (info) => { console.log('LayoutWithMenu', info); if (!info.schema) { @@ -58,6 +84,7 @@ function LayoutWithMenu({ schema }) { }} /> + - - - - + + + + + + + ); } diff --git a/packages/client/src/components/admin-layout/style.less b/packages/client/src/components/admin-layout/style.less index 039461cd0..5259254dc 100644 --- a/packages/client/src/components/admin-layout/style.less +++ b/packages/client/src/components/admin-layout/style.less @@ -47,3 +47,10 @@ } } } + +.nb-designable-toggle { + background: none !important; + &.active { + background: #1890ff !important; + } +} \ No newline at end of file diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx index 207ae7638..4c95a686a 100644 --- a/packages/client/src/components/schema-renderer/index.tsx +++ b/packages/client/src/components/schema-renderer/index.tsx @@ -19,7 +19,7 @@ import { useMemo } from 'react'; import { CodeOutlined } from '@ant-design/icons'; import Editor from '@monaco-editor/react'; -import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd'; +import { ArrayCollapse, ArrayTable, FormLayout, FormItem as FormilyFormItem } from '@formily/antd'; import { Space, Card, Modal, Spin } from 'antd'; import { Action } from '../../schemas/action'; @@ -50,6 +50,7 @@ import { CardItem } from '../../schemas/card-item'; import { DragAndDrop } from '../../schemas/drag-and-drop'; import { TreeSelect } from '../../schemas/tree-select'; import { Page } from '../../schemas/page'; +import { useCollectionContext, useSwithDesignableContext } from '../../schemas'; export const BlockContext = createContext({ dragRef: null }); @@ -74,6 +75,7 @@ export const SchemaField = createSchemaField({ ArrayTable, FormLayout, TreeSelect, + FormilyFormItem, DragAndDrop, @@ -188,10 +190,12 @@ export function useSchemaComponent(component: string) { } export function useDesignable(path?: any) { + const { designable } = useSwithDesignableContext(); const { schema = new Schema({}), refresh } = useContext(DesignableContext); const schemaPath = path || useSchemaPath(); const fieldSchema = useFieldSchema(); - let currentSchema = + let current; + let currentSchema = current = findPropertyByPath(schema, schemaPath) || ({} as Schema); if (!currentSchema) { currentSchema = fieldSchema; @@ -204,6 +208,9 @@ export function useDesignable(path?: any) { const DesignableBar = get(options.components, currentSchema['x-designable-bar']) || (() => null); return { + designable, + root: schema, + currentSchema: current, DesignableBar, schema: currentSchema, refresh, @@ -325,10 +332,14 @@ export function useDesignable(path?: any) { } s.parent.removeProperty(s.name); removed.push(s); + if (s['x-component'] === 'Grid.Row') { + return; + } if (Object.keys(s.parent.properties || {}).length === 0) { remove(s.parent); } }; + console.log({ removed }) remove(target); refresh(); return removed; @@ -353,12 +364,12 @@ export function getSchemaPath(schema: Schema) { const path = schema['x-path'] || [schema.name]; let parent = schema.parent; while (parent) { - if (!parent.name) { - break; - } + // if (!parent.name) { + // break; + // } if (parent['x-path']) { path.unshift(...parent['x-path']); - } else { + } else if (parent.name) { path.unshift(parent.name); } parent = parent.parent; @@ -434,6 +445,7 @@ export interface SchemaRendererProps { } export const SchemaRenderer = (props: SchemaRendererProps) => { + const { designable } = useSwithDesignableContext(); const [, refresh] = useState(uid()); const form = useMemo(() => props.form || createForm(), []); const schema = useMemo(() => { @@ -456,6 +468,10 @@ export const SchemaRenderer = (props: SchemaRendererProps) => { } return new Schema(s); }, []); + // useEffect(() => { + // refresh(uid()); + // }, [designable]); + console.log({ designable }, 'designable') const defaultRender = ({ schema }) => ( {} + const RouteSwitch = createRouteSwitch({ components: { AdminLayout, diff --git a/packages/client/src/schemas/action/index.tsx b/packages/client/src/schemas/action/index.tsx index c0833c7c8..e454e68a6 100644 --- a/packages/client/src/schemas/action/index.tsx +++ b/packages/client/src/schemas/action/index.tsx @@ -30,7 +30,7 @@ export const Action: any = observer((props: any) => { const isDropdownOrPopover = child && ['Action.Dropdown', 'Action.Popover'].includes(child['x-component']); - console.log({ DesignableBar, schema }); + // console.log({ DesignableBar, schema }); return ( {!isDropdownOrPopover && ( diff --git a/packages/client/src/schemas/add-new/index.tsx b/packages/client/src/schemas/add-new/index.tsx index bc3e2b663..aa5865ccf 100644 --- a/packages/client/src/schemas/add-new/index.tsx +++ b/packages/client/src/schemas/add-new/index.tsx @@ -47,6 +47,7 @@ import { DatabaseOutlined, } from '@ant-design/icons'; import { + createCollectionField, createOrUpdateCollection, createSchema, request, @@ -57,6 +58,9 @@ import { import { uid } from '@formily/shared'; import { useRequest } from 'ahooks'; import { SchemaField } from '../../components/schema-renderer'; +import { useFormContext } from '../form'; +import { cloneDeep } from 'lodash'; +import { options } from '../database-field/interfaces'; const generateGridBlock = (schema: ISchema) => { const name = schema.name || uid(); @@ -429,6 +433,9 @@ function generateCardItemSchema(component) { name: uid(), 'x-decorator': 'CardItem', 'x-component': 'Form', + 'x-component-props': { + showDefaultButtons: true, + }, 'x-designable-bar': 'Form.DesignableBar', properties: { [uid()]: { @@ -491,9 +498,14 @@ 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, - }); + // const { + // data: collections = [], + // loading, + // run, + // } = useRequest('collections:findAll', { + // formatResult: (result) => result?.data, + // }); + const { data: collections = [], loading, refresh } = useCollectionContext(); console.log({ collections }); return ( { minWidth: 200, }} onVisibleChange={(visible) => { - if (visible) { - run(); - } console.log('onVisibleChange', visible); }} placement={'bottomCenter'} @@ -511,6 +520,7 @@ AddNew.CardItem = observer((props: any) => { { let data: ISchema; + let collectionName = null; if (['addNewTable', 'addNewForm'].includes(info.key)) { const values = await FormDialog(`新建数据表`, () => { return ( @@ -525,13 +535,16 @@ AddNew.CardItem = observer((props: any) => { }, }); await createOrUpdateCollection(values); + refresh(); data = generateCardItemSchema( info.key === 'addNewTable' ? 'Table' : 'Form', ); + collectionName = values.name; } else if (info.key !== 'Markdown') { const keys = info.key.split('.'); const component = keys.shift(); const tableName = keys.join('.'); + collectionName = tableName; data = generateCardItemSchema(component); console.log('info.keyPath', component, tableName); } else { @@ -540,6 +553,10 @@ AddNew.CardItem = observer((props: any) => { if (schema['key']) { data['key'] = uid(); } + if (collectionName) { + data['x-component-props'] = data['x-component-props'] || {}; + data['x-component-props']['collectionName'] = collectionName; + } if (isGridBlock(schema)) { path.pop(); path.pop(); @@ -669,8 +686,9 @@ AddNew.BlockItem = observer((props: any) => { AddNew.FormItem = observer((props: any) => { const { ghost, defaultAction } = props; - const { schema, insertBefore, insertAfter } = useDesignable(); + const { schema, insertBefore, insertAfter, appendChild } = useDesignable(); const path = useSchemaPath(); + const { collection = {}, refresh } = useFormContext(); return ( { placement={'bottomCenter'} overlay={ - - { - 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') { - s = insertAfter(data, [...path]); - } else { - s = insertBefore(data, [...path]); + + {collection?.fields?.map((field) => ( + { + 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); } - await createSchema(s); - } - }} - > - 字段 1 - - - 字段2 - - - 字段3 - - - 字段4 - - - 字段5 - - - 字段6 - - - 字段7 - + 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); + } + }} + > + {field?.uiSchema?.title || '未命名'} + + ))} - - - 单行文本 - - 多行文本 - 手机号 - 数字 - 说明文本 + + {options.map((option) => ( + + {option.children.map((item) => ( + { + const values = await FormDialog(`新增字段`, () => { + return ( + + + + ); + }).open({ + initialValues: { + ...item.default, + key: uid(), + name: `f_${uid()}`, + }, + }); + await createCollectionField(collection.name, values); + await refresh(); + let data: ISchema = cloneDeep(values.uiSchema); + data['name'] = values.name; + data['referenceKey'] = data['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); + } + }} + > + {item.title} + + ))} + + ))} + + { + let data: ISchema = { + key: uid(), + type: 'string', + default: '这是一段演示文字', + 'x-designable-bar': 'Markdown.FormItemDesignableBar', + 'x-decorator': 'FormItem', + 'x-read-pretty': true, + 'x-component': 'Markdown', + 'x-component-props': { + savedInSchema: true, + }, + }; + 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); + } + }} + > + 添加说明文字 + } > diff --git a/packages/client/src/schemas/block-item/index.tsx b/packages/client/src/schemas/block-item/index.tsx index 45b3394ca..597c4826c 100644 --- a/packages/client/src/schemas/block-item/index.tsx +++ b/packages/client/src/schemas/block-item/index.tsx @@ -39,7 +39,7 @@ const DraggableBlock = (props) => {
{ setActive(true); - console.log('e.onMouseEnter', new Date().toString()); + // console.log('e.onMouseEnter', new Date().toString()); }} onMouseMove={(event) => { let dropElement = document.elementFromPoint( @@ -65,11 +65,11 @@ const DraggableBlock = (props) => { if (dropIds.length > 0) { setActive(dropIds[0] === schema.name); } - console.log('e.onMouseMove', dropIds, schema.name); + // console.log('e.onMouseMove', dropIds, schema.name); }} onMouseLeave={(e) => { setActive(false); - console.log('e.onMouseLeave', new Date().toString()); + // console.log('e.onMouseLeave', new Date().toString()); }} ref={mergeRefs([previewRef, dropRef])} className={cls('nb-grid-block', 'designable-form-item', { diff --git a/packages/client/src/schemas/database-field/interfaces/select.ts b/packages/client/src/schemas/database-field/interfaces/select.ts index ff28f3861..9d981e9b0 100644 --- a/packages/client/src/schemas/database-field/interfaces/select.ts +++ b/packages/client/src/schemas/database-field/interfaces/select.ts @@ -6,12 +6,15 @@ export const select: ISchema = { title: '下拉选择', group: 'choices', default: { + interface: 'select', dataType: 'string', // name, uiSchema: { type: 'string', // title, 'x-component': 'Select', + 'x-decorator': 'FormItem', + 'x-designable-bar': 'Markdown.DesignableBar', enum: [], } as ISchema, }, diff --git a/packages/client/src/schemas/database-field/interfaces/string.ts b/packages/client/src/schemas/database-field/interfaces/string.ts index c88e7649b..b8eea8c9e 100644 --- a/packages/client/src/schemas/database-field/interfaces/string.ts +++ b/packages/client/src/schemas/database-field/interfaces/string.ts @@ -6,12 +6,15 @@ export const string: ISchema = { title: '单行文本', group: 'basic', default: { + interface: 'string', dataType: 'string', // name, uiSchema: { type: 'string', // title, 'x-component': 'Input', + 'x-decorator': 'FormItem', + 'x-designable-bar': 'Input.DesignableBar', } as ISchema, }, properties: { diff --git a/packages/client/src/schemas/database-field/interfaces/subTable.ts b/packages/client/src/schemas/database-field/interfaces/subTable.ts index cfd624794..44a96eaca 100644 --- a/packages/client/src/schemas/database-field/interfaces/subTable.ts +++ b/packages/client/src/schemas/database-field/interfaces/subTable.ts @@ -6,11 +6,14 @@ export const subTable: ISchema = { title: '子表格', group: 'relation', default: { + interface: 'subTable', // name, uiSchema: { type: 'string', // title, - 'x-component': 'Select', + 'x-decorator': 'FormItem', + 'x-component': 'Table', + 'x-designable-bar': 'Markdown.DesignableBar', enum: [], } as ISchema, }, diff --git a/packages/client/src/schemas/database-field/interfaces/textarea.ts b/packages/client/src/schemas/database-field/interfaces/textarea.ts index 85742ebff..109a25b60 100644 --- a/packages/client/src/schemas/database-field/interfaces/textarea.ts +++ b/packages/client/src/schemas/database-field/interfaces/textarea.ts @@ -11,7 +11,9 @@ export const textarea: ISchema = { uiSchema: { type: 'string', // title, + 'x-decorator': 'FormItem', 'x-component': 'Input.TextArea', + 'x-designable-bar': 'Markdown.DesignableBar', } as ISchema, }, properties: { diff --git a/packages/client/src/schemas/form/DesignableBar.tsx b/packages/client/src/schemas/form/DesignableBar.tsx new file mode 100644 index 000000000..a2bbd4e04 --- /dev/null +++ b/packages/client/src/schemas/form/DesignableBar.tsx @@ -0,0 +1,94 @@ +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, + useCollectionContext, +} from '../'; +import get from 'lodash/get'; +import { Button, 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'; +import constate from 'constate'; +import { useEffect } from 'react'; +import { uid } from '@formily/shared'; +import { getSchemaPath } from '../../components/schema-renderer'; + +export const DesignableBar = observer((props) => { + const field = useField(); + const { designable, schema, refresh, deepRemove } = useDesignable(); + const [visible, setVisible] = useState(false); + const { dragRef } = useContext(DraggableBlockContext); + if (!designable) { + return null; + } + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + + {dragRef && } + { + setVisible(visible); + }} + overlay={ + + { + field.readPretty = true; + }} + > + 编辑表单配置 + + + { + const removed = deepRemove(); + // console.log({ removed }) + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + }} + > + 删除当前表单 + + + } + > + + + + +
+ ); +}); diff --git a/packages/client/src/schemas/form/Field.DesignableBar.tsx b/packages/client/src/schemas/form/Field.DesignableBar.tsx new file mode 100644 index 000000000..3bd7edd3e --- /dev/null +++ b/packages/client/src/schemas/form/Field.DesignableBar.tsx @@ -0,0 +1,114 @@ +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, + useCollectionContext, + updateSchema, +} from '../'; +import get from 'lodash/get'; +import { Button, 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'; +import constate from 'constate'; +import { useEffect } from 'react'; +import { uid } from '@formily/shared'; +import { getSchemaPath } from '../../components/schema-renderer'; +import { FormFieldUIDContext } from '.'; + +export const FieldDesignableBar = observer((props) => { + const field = useField(); + const fieldSchema = useFieldSchema(); + const { designable, schema, deepRemove } = useDesignable(); + const [visible, setVisible] = useState(false); + const { dragRef } = useContext(DraggableBlockContext); + const fieldUid = useContext(FormFieldUIDContext); + const fieldName = schema['x-component-props']?.['fieldName']; + if (!designable) { + return null; + } + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + + {dragRef && } + { + setVisible(visible); + }} + overlay={ + + { + const title = uid(); + field + .query(field.address.concat(fieldUid, fieldName)) + .take((f) => { + f.title = title; + }); + console.log( + 'fieldfieldfieldfieldfield', + field.address.concat(fieldUid, fieldName).entire, + schema, + ); + schema['title'] = title; + await updateSchema({ + key: schema['key'], + title, + }); + }} + > + 修改字段配置 + + + { + const removed = deepRemove(); + console.log({ schema, removed }); + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + }} + > + 删除当前字段 + + + } + > + + + + +
+ ); +}); diff --git a/packages/client/src/schemas/form/index.md b/packages/client/src/schemas/form/index.md index 918f80273..d591f89c7 100644 --- a/packages/client/src/schemas/form/index.md +++ b/packages/client/src/schemas/form/index.md @@ -20,6 +20,13 @@ group: // 添加其他节点 +
+ // Form.Field 可用于覆盖字段原配置,但不修改原字段配置,新配置只作用于当前 Form + + + +
+ // Form 用作 decorator 时只用于创建独立的作用域 diff --git a/packages/client/src/schemas/form/index.tsx b/packages/client/src/schemas/form/index.tsx index 402bfb9bf..49a35ebaa 100644 --- a/packages/client/src/schemas/form/index.tsx +++ b/packages/client/src/schemas/form/index.tsx @@ -9,10 +9,18 @@ import { FormProvider, ISchema, useField, + useForm, + RecursionField, } from '@formily/react'; -import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../'; +import { + useSchemaPath, + SchemaField, + useDesignable, + removeSchema, + useCollectionContext, +} from '../'; import get from 'lodash/get'; -import { Dropdown, Menu, Space } from 'antd'; +import { Button, Dropdown, Menu, Space } from 'antd'; import { MenuOutlined, DragOutlined } from '@ant-design/icons'; import cls from 'classnames'; import { FormLayout } from '@formily/antd'; @@ -20,9 +28,34 @@ import './style.less'; import AddNew from '../add-new'; import { DraggableBlockContext } from '../../components/drag-and-drop'; import { isGridRowOrCol } from '../grid'; +import constate from 'constate'; +import { useEffect } from 'react'; +import { uid } from '@formily/shared'; +import { getSchemaPath } from '../../components/schema-renderer'; +import { DesignableBar } from './DesignableBar'; +import { FieldDesignableBar } from './Field.DesignableBar'; +import { createContext } from 'react'; +import { BlockItem } from '../block-item'; + +const [FormContextProvider, useFormContext] = constate(({ schema, form }) => { + // const schema = useFieldSchema(); + const collectionName = schema['x-component-props']?.['collectionName']; + const { data: collections = [], loading, refresh } = useCollectionContext(); + const collection = collectionName + ? collections.find((item) => item.name === collectionName) + : {}; + console.log({ collection }); + return { form, schema, collection, refresh }; +}); + +export { FormContextProvider, useFormContext }; export const Form: any = observer((props: any) => { - const { useValues = () => ({}), ...others } = props; + const { + useValues = () => ({}), + showDefaultButtons = false, + ...others + } = props; const initialValues = useValues(); const form = useMemo(() => { console.log('Form.useMemo', initialValues); @@ -33,91 +66,105 @@ export const Form: any = observer((props: any) => { const scope = useContext(SchemaExpressionScopeContext); return ( - {schema['x-decorator'] === 'Form' ? ( - - ) : ( - + {/* @ts-ignore */} + + {schema['x-decorator'] === 'Form' ? ( - - )} + ) : ( + + + + )} + {showDefaultButtons && ( + + + + + )} + ); }); -Form.__Decorator = ({ children }) => children; +export const FormFieldUIDContext = createContext(null); -Form.DesignableBar = observer((props) => { - const field = useField(); - const { schema, refresh, deepRemove } = useDesignable(); - const [visible, setVisible] = useState(false); - const { dragRef } = useContext(DraggableBlockContext); +Form.Field = observer((props: any) => { + const { fieldName } = props; + const { schema } = useDesignable(); + const path = getSchemaPath(schema); + console.log('getSchemaPath', path); + const { collection = {} } = useFormContext(); + if (!fieldName) { + return null; + } + const field = collection?.fields?.find((item) => item.name === fieldName); + if (!field) { + return null; + } + const name = useContext(FormFieldUIDContext); + const title = schema['title'] || field.uiSchema['title']; 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); - } - }} - > - 删除当前区块 - - - } - > - - - - -
+ ); }); + +Form.Field.Item = observer((props) => { + const name = uid(); + return ( + + {props.children} + + ); +}); + +Form.__Decorator = ({ children }) => children; +Form.DesignableBar = DesignableBar; +Form.Field.DesignableBar = FieldDesignableBar; diff --git a/packages/client/src/schemas/grid/index.tsx b/packages/client/src/schemas/grid/index.tsx index 0bf320e8a..3e8a7f3d0 100644 --- a/packages/client/src/schemas/grid/index.tsx +++ b/packages/client/src/schemas/grid/index.tsx @@ -89,6 +89,7 @@ export const Grid: any = observer((props: any) => { // const schema = useFieldSchema(); const gridPath = useSchemaPath(); const { + designable, schema, schema: designableSchema, insertAfter, @@ -166,7 +167,7 @@ export const Grid: any = observer((props: any) => { ); })} - {AddNewComponent && } + {designable && AddNewComponent && } ); @@ -187,7 +188,7 @@ Grid.Row = observer((props) => { deepRemove, } = useDesignable(); const len = Object.keys(designableSchema.properties || {}).length; - console.log({ len, schema, designableSchema }); + // console.log({ len, schema, designableSchema }); return ( { - const field = useField() + const field = useField(); if (!isVoidField(field) && field?.pattern === 'readPretty') { return React.createElement(component, { ...readPrettyProps, ...props, - }) + }); } - return React.createElement(target, props) + return React.createElement(target, props); }, { forwardRef: true, - } - ) + }, + ); }; } @@ -57,6 +57,13 @@ export async function createOrUpdateCollection(data: any) { }); } +export async function createCollectionField(collectionName: string, data: any) { + return await request(`collections/${collectionName}/fields:create`, { + method: 'post', + data, + }); +} + export async function deleteCollection(name) { await request('collections:destroy', { method: 'post', @@ -109,3 +116,16 @@ const [CollectionContextProvider, useCollectionContext] = constate(() => { }); export { CollectionContextProvider, useCollectionContext }; + +const [SwithDesignableContextProvider, useSwithDesignableContext] = constate(() => { + const [active, setActive] = useCookieState('useCookieDesignable'); + + return { + designable: active === 'true', + setDesignable(value) { + setActive(value ? 'true' : 'false'); + } + } +}); + +export { SwithDesignableContextProvider, useSwithDesignableContext }; diff --git a/packages/client/src/schemas/input/index.tsx b/packages/client/src/schemas/input/index.tsx index 4a5762c16..35290da38 100644 --- a/packages/client/src/schemas/input/index.tsx +++ b/packages/client/src/schemas/input/index.tsx @@ -1,10 +1,19 @@ import React from 'react' -import { connect, mapProps, mapReadPretty } from '@formily/react' +import { connect, mapProps, mapReadPretty, observer, useField, useFieldSchema } from '@formily/react' import { Input as AntdInput } from 'antd' import { InputProps, TextAreaProps } from 'antd/lib/input' -import { LoadingOutlined } from '@ant-design/icons' import { Display } from '../display'; import { DesignableBar } from './DesignableBar'; +import { Dropdown, Menu, Space } from 'antd'; +import { LoadingOutlined, MenuOutlined, DragOutlined } from '@ant-design/icons'; +import { getSchemaPath, 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'; type ComposedInput = React.FC & { TextArea?: React.FC @@ -15,6 +24,7 @@ type ComposedInput = React.FC & { export const Input: ComposedInput = connect( AntdInput, mapProps((props, field) => { + console.log('fieldfieldfieldfieldfield', field.address.entire); return { ...props, suffix: ( @@ -31,8 +41,65 @@ export const Input: ComposedInput = connect( mapReadPretty(Display.Input) ) -Input.DesignableBar = DesignableBar; Input.TextArea = connect(AntdInput.TextArea, mapReadPretty(Display.TextArea)) Input.URL = connect(AntdInput, mapReadPretty(Display.URL)) +Input.DesignableBar = observer((props) => { + const field = useField(); + const fieldSchema = useFieldSchema(); + const { root, currentSchema, 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={ + + { + console.log({ root, currentSchema, schema, fieldSchema }, getSchemaPath(fieldSchema)); + }} + > + 修改 Input + + + { + const removed = deepRemove(); + // console.log({ removed }) + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + }} + > + 删除当前区块 + + + } + > + + + + +
+ ); +}); + export default Input diff --git a/packages/client/src/schemas/markdown/index.tsx b/packages/client/src/schemas/markdown/index.tsx index 270fbb4e8..d385cca17 100644 --- a/packages/client/src/schemas/markdown/index.tsx +++ b/packages/client/src/schemas/markdown/index.tsx @@ -20,6 +20,7 @@ import { DraggableBlockContext } from '../../components/drag-and-drop'; import { uid } from '@formily/shared'; import { removeSchema, updateSchema } from '..'; import { isGridRowOrCol } from '../grid'; +import './style.less'; export const Markdown: any = connect( (props) => { @@ -67,7 +68,7 @@ export const Markdown: any = connect( console.log('Markdown', props); let text = props.value; let value = ( -
+
); return ; }), @@ -75,9 +76,12 @@ export const Markdown: any = connect( Markdown.DesignableBar = observer((props) => { const field = useField(); - const { schema, refresh, deepRemove } = useDesignable(); + const { designable, schema, refresh, deepRemove } = useDesignable(); const [visible, setVisible] = useState(false); const { dragRef } = useContext(DraggableBlockContext); + if (!designable) { + return null; + } return (
{ } }} > - 删除当前区块 + 删除当前文本 + + + } + > + + + + +
+ ); +}); + +Markdown.FormItemDesignableBar = observer((props) => { + const field = useField(); + const { designable, schema, refresh, deepRemove } = useDesignable(); + const [visible, setVisible] = useState(false); + const { dragRef } = useContext(DraggableBlockContext); + if (!designable) { + return null; + } + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + + {dragRef && } + { + setVisible(visible); + }} + overlay={ + + { + field.readPretty = false; + }} + > + 修改文本内容 + + + { + const removed = deepRemove(); + // console.log({ removed }) + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + }} + > + 删除当前文本 } diff --git a/packages/client/src/schemas/markdown/style.less b/packages/client/src/schemas/markdown/style.less new file mode 100644 index 000000000..64bd143c9 --- /dev/null +++ b/packages/client/src/schemas/markdown/style.less @@ -0,0 +1,3 @@ +.nb-markdown > *:last-child { + margin-bottom: 0; +} \ No newline at end of file diff --git a/packages/client/src/schemas/menu/index.tsx b/packages/client/src/schemas/menu/index.tsx index d383f0c54..ab8c4309e 100644 --- a/packages/client/src/schemas/menu/index.tsx +++ b/packages/client/src/schemas/menu/index.tsx @@ -97,7 +97,7 @@ const SideMenu = (props: any) => { export const Menu: any = observer((props: any) => { const { mode, onSelect, sideMenuRef, ...others } = props; - const { schema } = useDesignable(); + const { designable, schema } = useDesignable(); const fieldSchema = useFieldSchema(); console.log('Menu.schema', schema, fieldSchema); const [selectedKey, setSelectedKey] = useState(null); @@ -275,8 +275,10 @@ Menu.SubMenu = observer((props: any) => { }); Menu.AddNew = observer((props: any) => { - const { appendChild } = useDesignable(props.path); - + const { designable, appendChild } = useDesignable(props.path); + if (!designable) { + return null; + } const schemas = { 'Menu.Link': { icon: , @@ -516,11 +518,15 @@ Menu.DesignableBar = (props) => { const field = useField(); const [visible, setVisible] = useState(false); - const { schema, remove, refresh, insertAfter, insertBefore, appendChild } = + const { designable, schema, remove, refresh, insertAfter, insertBefore, appendChild } = useDesignable(); const formConfig = schemas[schema['x-component']]; const isSubMenu = schema['x-component'] === 'Menu.SubMenu'; + if (!designable) { + return null; + } + return (
{ const schema = useFieldSchema(); const field = useField(); - console.log('Table.Column', schema, field.title); const { DesignableBar } = useDesignableBar(); return (
@@ -461,7 +461,6 @@ Table.Column.DesignableBar = () => { // const fieldSchema = useFieldSchema(); const { schema, remove, refresh, insertAfter } = useDesignable(); const [visible, setVisible] = useState(false); - console.log('Table.Column.DesignableBar', { schema }); return (
{ const schema = useFieldSchema(); const field = useField(); const path = useSchemaPath(); - return
#{index + 1}
; + return
{index + 1}
; }); Table.Addition = observer((props: any) => { @@ -605,7 +604,7 @@ Table.Action.DesignableBar = () => { const path = useSchemaPath(); const { schema, remove, refresh, insertAfter } = useDesignable(); const [visible, setVisible] = useState(false); - console.log('Table.Action.DesignableBar', path, field.address.entire, { schema, field }); + // console.log('Table.Action.DesignableBar', path, field.address.entire, { schema, field }); return (
{ Table.DesignableBar = observer((props) => { const field = useField(); - const { schema, refresh, deepRemove } = useDesignable(); + const { designable, schema, refresh, deepRemove } = useDesignable(); const [visible, setVisible] = useState(false); const { dragRef } = useContext(DraggableBlockContext); + if (!designable) { + return null; + } return (
{ { - deepRemove(); + onClick={async () => { + const removed = deepRemove(); + // console.log({ removed }) + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } }} > 删除当前区块