diff --git a/packages/client/src/fields/designer/index.md b/packages/client/src/fields/designer/index.md index 9ff0765c6..bae940840 100644 --- a/packages/client/src/fields/designer/index.md +++ b/packages/client/src/fields/designer/index.md @@ -11,3 +11,14 @@ group: # Designer - 字段设计器 + +```tsx +import React from 'react'; +import Designer from './' + +export default () => { + return ( + + ) +} +``` \ No newline at end of file diff --git a/packages/client/src/fields/designer/index.tsx b/packages/client/src/fields/designer/index.tsx index e69de29bb..ce4567cb4 100644 --- a/packages/client/src/fields/designer/index.tsx +++ b/packages/client/src/fields/designer/index.tsx @@ -0,0 +1,527 @@ +import React, { useMemo, useContext, createContext, useState } from 'react'; +import { Form, FormItem, FormLayout, Input } from '@formily/antd'; +import { + observer, + FormProvider, + FormConsumer, + createSchemaField, + useField, + useFieldSchema, + useForm, + Schema, + SchemaExpressionScopeContext, + SchemaKey, + connect, + mapReadPretty, + mapProps, +} from '@formily/react'; +import { createForm } from '@formily/core'; +import { Row, Col, Button } from 'antd'; +import { uid } from '@formily/shared'; +import { useDrop, useDrag } from 'ahooks'; +import './style.less'; +import { DeleteOutlined, MenuOutlined, PlusOutlined } from '@ant-design/icons'; + +const SchemaEditorContext = createContext(new Schema({})); +const SchemaRefreshContext = createContext(null); + +function DropRow() { + const { schema: rowSchema, refresh } = useSchema(); + const schema = rowSchema.parent; + const context = useContext(SchemaEditorContext); + function getSchema(names: any[]) { + const paths = [...names]; + let schema: Schema = context; + while (paths.length) { + schema = schema.properties[paths.shift()]; + } + return schema; + } + const [props, { isHovering }] = useDrop({ + onDom: (content, e) => { + console.log('paths', content); + const source = getSchema(content.paths); + // console.log({ paths: content.paths, source }); + // schema.addProperty(source.name, source.toJSON()); + // source.parent.removeProperty(source.name); + // refresh(); + + const rowName = `row_${uid()}`; + const colName = `col_${uid()}`; + const fieldName = `field_${uid()}`; + schema.addProperty(rowName, { + type: 'void', + name: rowName, + 'x-component': 'GridRow', + properties: { + [colName]: { + type: 'void', + name: colName, + 'x-component': 'GridColumn', + 'x-component-props': { + span: 24, + }, + properties: { + [source.name]: source.toJSON(), + }, + }, + }, + }); + refresh(); + }, + }); + + return ( +
+ ); +} + +function DropRowColumn() { + const form = useForm(); + const { schema, refresh } = useSchema(); + const context = useContext(SchemaEditorContext); + function getSchema(names: any[]) { + const paths = [...names]; + let schema: Schema = context; + while (paths.length) { + schema = schema.properties[paths.shift()]; + } + return schema; + } + const [props, { isHovering }] = useDrop({ + onDom: (content, e) => { + // console.log('paths', content); + const source = getSchema(content.paths); + const col = `col${uid()}`; + const blockName = `block${uid()}`; + const inputName = `input${uid()}`; + schema.addProperty(col, { + type: 'void', + name: col, + 'x-component': 'GridColumn', + 'x-component-props': { + labelCol: 6, + wrapperCol: 10, + }, + properties: { + [blockName]: source.toJSON(), + }, + }); + const len = Object.keys(schema.properties).length; + const properties = schema.mapProperties((p) => p); + + // schema.setProperties(newProperties); + // console.log(properties); + refresh(); + + properties.forEach((p) => { + form.setFieldState(getFullPaths(p).join('.'), (state) => { + state.componentProps = { + span: 24 / len, + }; + }); + }); + + // console.log({ paths: content.paths, source }); + // schema.addProperty(source.name, source.toJSON()); + // source.parent.removeProperty(source.name); + // refresh(); + }, + }); + + return ( +
+ ); +} + +function DropColumn() { + const form = useForm(); + const { schema: columnSchema, refresh } = useSchema(); + const schema = columnSchema.parent; + const context = useContext(SchemaEditorContext); + function getSchema(names: any[]) { + const paths = [...names]; + let s: Schema = context; + while (paths.length) { + s = s.properties[paths.shift()]; + } + return s; + } + const [props, { isHovering }] = useDrop({ + onDom: (content, e) => { + // console.log('paths', content); + const source = getSchema(content.paths); + const col = `col${uid()}`; + const blockName = `block${uid()}`; + const inputName = `input${uid()}`; + schema.addProperty(col, { + type: 'void', + name: col, + 'x-component': 'GridColumn', + 'x-component-props': { + labelCol: 6, + wrapperCol: 10, + }, + properties: { + [blockName]: source.toJSON(), + }, + }); + const len = Object.keys(schema.properties).length; + const properties = schema.mapProperties((p) => p); + + // schema.setProperties(newProperties); + // console.log(properties); + refresh(); + + properties.forEach((p) => { + form.setFieldState(getFullPaths(p).join('.'), (state) => { + state.componentProps = { + span: 24 / len, + }; + }); + }); + + // console.log({ paths: content.paths, source }); + // schema.addProperty(source.name, source.toJSON()); + // source.parent.removeProperty(source.name); + // refresh(); + }, + }); + + return ( +
+ ); +} + +function DropBlock() { + const { schema, refresh } = useSchema(); + const context = useContext(SchemaEditorContext); + function getSchema(names: any[]) { + const paths = [...names]; + let s: Schema = context; + while (paths.length) { + s = s.properties[paths.shift()]; + } + return s; + } + const [props, { isHovering }] = useDrop({ + onDom: (content, e) => { + const source = getSchema(content.paths); + console.log({ paths: content.paths, source }); + schema.parent.addProperty(source.name, source.toJSON()); + source.parent.removeProperty(source.name); + refresh(); + }, + }); + + return ( +
+ ); +} + +export const Grid = ({ children }) => { + const { schema, refresh } = useSchema(); + return ( +
+ {children} + +
+ ); +}; + +export const GridRow = (props) => { + console.log('GridRow', props); + const { children } = props; + const form = useForm(); + const { schema, refresh } = useSchema(); + return ( +
+ {children} + + +
+ ); +}; + +const getFullPaths = (schema: Schema) => { + if (!schema) { + return []; + } + const paths = [schema.name]; + if (schema.parent.name) { + paths.unshift(...getFullPaths(schema.parent)); + } + return paths; +}; + +function useSchema() { + const context = useContext(SchemaEditorContext); + const refresh = useContext(SchemaRefreshContext); + const fieldSchema = useFieldSchema(); + const paths = getFullPaths(fieldSchema); + console.log({ fieldSchema, paths, context }); + let schema: Schema = context; + const names = [...paths]; + while (names.length) { + schema = schema.properties[names.shift()]; + } + return { schema, fieldSchema, refresh }; +} + +export const GridColumn = (props) => { + const { children } = props; + const { schema, refresh } = useSchema(); + console.log('GridColumn', { props }); + return ( + + + {children} + + ); +}; + +function GridBlock({ children, ...props }) { + const fieldSchema = useFieldSchema(); + const { schema, refresh } = useSchema(); + const field = useField(); + console.log('x-read-pretty', field.readPretty); + const getDragProps = useDrag({ + onDragStart: (data, e) => { + // e.dataTransfer + // setDragging(data); + }, + onDragEnd: (data) => { + // setDragging(null); + }, + }); + const paths = getFullPaths(schema); + console.log({ props, paths, schema, fieldSchema }); + return ( +
+
+ { + schema.parent.removeProperty(schema.name); + refresh(); + }} + /> + +
+ {children} + +
+ ); +} + +function G({ children }) { + return
{children}
; +} + +export const SchemaField = createSchemaField({ + components: { + FormLayout, + FormItem, + Input, + Grid, + GridRow, + GridColumn, + GridBlock: (props) => { + const field = useField(); + return React.createElement(field.readPretty ? G : GridBlock, props); + }, + }, +}); + +const schema = new Schema({ + type: 'object', + properties: { + layout: { + type: 'void', + 'x-component': 'FormLayout', + 'x-component-props': { + layout: 'vertical', + // labelCol: 6, + // wrapperCol: 10, + }, + properties: { + grid1: { + type: 'void', + 'x-component': 'Grid', + 'x-component-props': {}, + properties: { + row1: { + type: 'void', + 'x-component': 'GridRow', + 'x-component-props': { + labelCol: 6, + wrapperCol: 10, + }, + properties: { + col1: { + type: 'void', + 'x-component': 'GridColumn', + 'x-read-pretty': true, + 'x-component-props': { + labelCol: 6, + wrapperCol: 10, + span: 12, + }, + properties: { + block1: { + type: 'void', + 'x-component': 'GridBlock', + 'x-component-props': {}, + 'x-read-pretty': false, + properties: { + input1: { + type: 'string', + title: '输入框1', + required: true, + 'x-read-pretty': false, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + block2: { + type: 'void', + 'x-component': 'GridBlock', + 'x-component-props': {}, + 'x-read-pretty': false, + properties: { + input2: { + type: 'string', + title: '输入框2', + required: true, + 'x-read-pretty': false, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + }, + }, + col2: { + type: 'void', + 'x-component': 'GridColumn', + 'x-component-props': { + span: 12, + labelCol: 6, + wrapperCol: 10, + }, + properties: { + block3: { + type: 'void', + 'x-component': 'GridBlock', + 'x-component-props': {}, + 'x-read-pretty': false, + properties: { + input3: { + type: 'string', + title: '输入框3', + required: true, + 'x-read-pretty': false, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +}); + +function SchemaProvider(props) { + const { schema } = props; + const [, forceUpdate] = useState(0); + return ( + { + forceUpdate(Math.random()); + }} + > + + + + + ); +} + +export function Designer() { + const form = useMemo(() => createForm({}), []); + const [, forceUpdate] = useState(0); + return ( +
+ + + + {(form) => { + return
{JSON.stringify(form.values, null, 2)}
; + }} +
+
+
+ ); +} + +export default Designer; diff --git a/packages/client/src/fields/designer/style.less b/packages/client/src/fields/designer/style.less new file mode 100644 index 000000000..fea7e0405 --- /dev/null +++ b/packages/client/src/fields/designer/style.less @@ -0,0 +1,57 @@ +.drop-column { + position: absolute; + top: 0; + left: -12px; + height: 100%; + width: 24px; + // border: 1px dashed rgb(232, 232, 232); + &.active { + background: #e6f7ff; + } + &.last { + left: auto; + right: -24px; + } +} +.drop-block { + height: 12px; + // background: #e6f7ff; + // pointer-events: none; + &.active { + background: #e6f7ff; + } +} +.ant-formily-item + .drop-block { + position: absolute; + bottom: 12px; + width: 100%; +} + +.grid-block { + position: relative; + padding-bottom: 1px; + .grid-block-actions { + position: absolute; + top: 0; + right: 0; + z-index: 1; + // display: none; + } + &:hover { + .grid-block-actions { + display: inline-block; + } + } +} + +.drop-row { + height: 12px; + // background: #e6f7ff; + position: absolute; + bottom: 0; + width: 100%; + &.active { + background: #e6f7ff; + } +} +