diff --git a/packages/client/package.json b/packages/client/package.json
index 98e605d66..bb6699f0d 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -24,6 +24,10 @@
"ahooks": "^2.10.2",
"axios": "^0.21.1",
"lodash": "^4.17.21",
+ "react-dnd": "^14.0.2",
+ "react-dnd-html5-backend": "^14.0.0",
+ "react-dnd-preview": "^6.0.2",
+ "react-dnd-touch-backend": "^14.0.0",
"react-helmet": "^6.1.0",
"react-hooks-global-state": "^1.0.1",
"umi-request": "^1.3.5"
diff --git a/packages/client/src/blocks/block/index.md b/packages/client/src/blocks/block/index.md
deleted file mode 100644
index 151e0e057..000000000
--- a/packages/client/src/blocks/block/index.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-title: Block - 区块
-nav:
- title: 组件
- path: /client
-group:
- order: 2
- title: Blocks
- path: /client/blocks
----
-
-# Block - 区块
-
-```tsx
-import React from 'react';
-import BlockEditor from './';
-
-const items = [
- {
- type: 'page',
- name: 'block1',
- },
- {
- type: 'page',
- name: 'block2',
- }
-]
-
-export default () => {
- return (
-
- )
-}
-```
diff --git a/packages/client/src/blocks/block/index.tsx b/packages/client/src/blocks/block/index.tsx
deleted file mode 100644
index d281e1b5f..000000000
--- a/packages/client/src/blocks/block/index.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import React from 'react';
-import { Dropdown, Menu, Card, Button, Popover } from 'antd';
-import { useDynamicList } from 'ahooks';
-import { MenuOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
-import './style.less';
-import { FormDialog, FormLayout } from '@formily/antd';
-import { SchemaField } from '../../fields';
-
-export function AddNewAction(props) {
- const { insert, children } = props;
- const menu = (
-
- );
- return {children || };
-}
-
-export function SettingAction(props) {
- const { remove } = props;
- const menu = (
-
- );
- return (
-
-
-
- );
-}
-
-export function BlockItem(props) {
- const { remove, insert, replace } = props;
- return (
-
- );
-}
-
-export default (props) => {
- const { list, push, remove, replace, insert } = useDynamicList(props.items);
- return (
-
- {list.map((item, index) => (
-
insert(index + 1, data)}
- replace={(data) => replace(index, data)}
- remove={() => remove(index)}
- key={index}
- schema={item}
- />
- ))}
-
- push(data)}>
- }>
-
-
- );
-};
diff --git a/packages/client/src/blocks/block/style.less b/packages/client/src/blocks/block/style.less
deleted file mode 100644
index 7ab49e1d6..000000000
--- a/packages/client/src/blocks/block/style.less
+++ /dev/null
@@ -1,20 +0,0 @@
-.block-list {
- background: #f0f2f5;
- padding: 0 24px 24px;
-}
-.block-item {
- position: relative;
- padding-top: 24px;
- .block-item-actions {
- display: none;
- position: absolute;
- top: 0;
- right: 0;
- z-index: 1;
- }
- &:hover {
- .block-item-actions {
- display: inline-block;
- }
- }
-}
\ No newline at end of file
diff --git a/packages/client/src/blocks/calendar/index.md b/packages/client/src/blocks/calendar/index.md
index 29935be6c..d244d9ad1 100644
--- a/packages/client/src/blocks/calendar/index.md
+++ b/packages/client/src/blocks/calendar/index.md
@@ -10,3 +10,10 @@ group:
---
# Calendar - 日历
+
+## 代码演示
+
+### 基本使用
+
+### 设计器模式
+
diff --git a/packages/client/src/blocks/chart/index.md b/packages/client/src/blocks/chart/index.md
index e1bd37d1c..71ddeeaf2 100644
--- a/packages/client/src/blocks/chart/index.md
+++ b/packages/client/src/blocks/chart/index.md
@@ -11,3 +11,9 @@ group:
# Chart - 图表
+## 代码演示
+
+### 基本使用
+
+### 设计器模式
+
diff --git a/packages/client/src/blocks/form/demos/demo1.tsx b/packages/client/src/blocks/form/demos/demo1.tsx
index 1b3ace2de..b1c7c5cd3 100644
--- a/packages/client/src/blocks/form/demos/demo1.tsx
+++ b/packages/client/src/blocks/form/demos/demo1.tsx
@@ -4,25 +4,27 @@
import React from 'react';
import { FormBlock } from '@nocobase/client';
+const fields = [
+ {
+ interface: 'string',
+ type: 'string',
+ title: `单行文本`,
+ name: 'username',
+ required: true,
+ default: 'abcdefg',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Input',
+ 'x-component-props': {
+ placeholder: 'please enter',
+ },
+ },
+];
+
export default () => {
return (
({
+ accept,
+ drop: () => ({
+ gridType: 'first-row',
+ schema,
+ segments: field.address.segments,
+ }),
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ }),
+ }),
+ [schema],
+ );
+
+ const active = canDrop && isOver;
+
+ return (
+
+ );
+}
+
+export function DropRow() {
+ const { schema } = useSchema();
+ const field = useField();
+ const accept = useContext(AcceptContext);
+ const [{ canDrop, isOver }, drop] = useDrop(
+ () => ({
+ accept,
+ drop: () => ({
+ gridType: 'row',
+ schema,
+ segments: field.address.segments,
+ }),
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ }),
+ }),
+ [schema],
+ );
+
+ const active = canDrop && isOver;
+
+ return ;
+}
+
+export function DropColumn() {
+ const field = useField();
+ const { schema } = useSchema();
+ const accept = useContext(AcceptContext);
+ const [{ canDrop, isOver }, drop] = useDrop(
+ () => ({
+ accept,
+ drop: () => ({
+ gridType: 'column',
+ schema,
+ segments: field.address.segments,
+ }),
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ }),
+ }),
+ [schema],
+ );
+
+ const active = canDrop && isOver;
+
+ return (
+
+ );
+}
+
+export function DropLastColumn() {
+ const field = useField();
+ const { schema } = useSchema();
+ const accept = useContext(AcceptContext);
+ const [{ canDrop, isOver }, drop] = useDrop(
+ () => ({
+ accept,
+ drop: () => ({
+ gridType: 'last-column',
+ schema,
+ segments: field.address.segments,
+ }),
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ }),
+ }),
+ [schema],
+ );
+
+ const active = canDrop && isOver;
+
+ return (
+
+ );
+}
+
+export function DropBlock({ canDrop }) {
+ const { schema } = useSchema();
+ const accept = useContext(AcceptContext);
+ const [{ isOver }, drop] = useDrop(
+ () => ({
+ accept,
+ drop: () => ({ schema }),
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ }),
+ canDrop: () => canDrop,
+ }),
+ [canDrop],
+ );
+
+ console.log({ canDrop });
+
+ const active = canDrop && isOver;
+
+ return (
+
+ );
+}
+
+export default {
+ DropRow,
+ DropColumn,
+ DropLastColumn,
+ DropBlock,
+};
diff --git a/packages/client/src/blocks/grid/Grid.tsx b/packages/client/src/blocks/grid/Grid.tsx
new file mode 100644
index 000000000..84cc50f76
--- /dev/null
+++ b/packages/client/src/blocks/grid/Grid.tsx
@@ -0,0 +1,319 @@
+import React, { createContext, useContext, useState } from 'react';
+import { Row, Col, Dropdown, Menu } from 'antd';
+import { DndProvider, useDrag, useDrop } from 'react-dnd';
+import { HTML5Backend } from 'react-dnd-html5-backend';
+import {
+ DropFirstRow,
+ DropRow,
+ DropColumn,
+ DropBlock,
+ DropLastColumn,
+} from './Drop';
+import {
+ CloseOutlined,
+ DeleteOutlined,
+ MenuOutlined,
+ PlusOutlined,
+ ArrowDownOutlined,
+ ArrowUpOutlined,
+} from '@ant-design/icons';
+import {
+ getFullPaths,
+ SchemaDesignerContext,
+ useSchemaQuery,
+ useSchema,
+} from '../../fields';
+import { TouchBackend } from 'react-dnd-touch-backend';
+import { usePreview } from 'react-dnd-preview';
+import classNames from 'classnames';
+import './style.less';
+import { useField, useFieldSchema } from '@formily/react';
+
+const Preview = () => {
+ const accept = useContext(AcceptContext);
+ const { item, style, display, itemType } = usePreview();
+ if (itemType !== accept) {
+ return null;
+ }
+ if (!display) {
+ return null;
+ }
+ if (!item.ref) {
+ return null;
+ }
+ if (!item.ref.current) {
+ return null;
+ }
+ const el = item.ref.current as HTMLDivElement;
+ console.log({ itemType });
+ return (
+
+ );
+};
+
+export interface GridPorps {
+ children?: React.ReactNode;
+}
+
+export interface GridRowPorps {
+ children?: React.ReactNode;
+ rowOrder?: number;
+}
+
+export interface GridColumnPorps {
+ children?: React.ReactNode;
+ span?: any;
+}
+
+export interface GridBlockProps {
+ children?: React.ReactNode;
+ lastComponentType?: string;
+}
+
+export type GridComponent = React.FC & {
+ Row?: React.FC;
+ Column?: React.FC;
+ Block?: React.FC;
+};
+
+export const AcceptContext = createContext(null);
+
+export const Grid: GridComponent = (props) => {
+ const { children } = props;
+ const schema = useFieldSchema();
+ const field = useField();
+ console.log({ accept: schema.name, schema: schema.toJSON() });
+ return (
+
+ );
+};
+
+Grid.Row = (props) => {
+ const { children, rowOrder } = props;
+ const { schema } = useSchema();
+ const field = useField();
+ const accept = useContext(AcceptContext);
+ console.log({ accept });
+ const [{ canDrop, isOverCurrent }, drop] = useDrop(() => ({
+ accept,
+ drop: (item, monitor) => {
+ const didDrop = monitor.didDrop();
+ if (didDrop) {
+ return;
+ }
+ return { gridType: 'row', schema, segments: field.address.segments };
+ },
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ isOverCurrent: monitor.isOver({ shallow: true }),
+ }),
+ }));
+
+ const active = canDrop && isOverCurrent;
+
+ return (
+
+ {rowOrder === 0 && }
+ {children}
+
+ {/* */}
+
+ );
+};
+
+Grid.Column = (props) => {
+ const { children, span } = props;
+ return (
+
+
+ {children}
+
+ );
+};
+
+interface DropResult {
+ [key: string]: any;
+}
+
+Grid.Block = (props) => {
+ const { children, lastComponentType } = props;
+ const { schema, refresh } = useSchema();
+ const fieldSchema = useFieldSchema();
+ const field = useField();
+ const accept = useContext(AcceptContext);
+ const context = useContext(SchemaDesignerContext);
+ const {
+ insertAfter,
+ insertAfterWithAddRow,
+ insertBeforeWithAddRow,
+ insertBeforeWithAddColumn,
+ appendToRowWithAddColumn,
+ } = useSchemaQuery();
+ const ref = React.useRef();
+ console.log({ accept });
+ const [{ opacity, isDragging }, drag, preview] = useDrag(
+ () => ({
+ type: accept,
+ item: {
+ ref,
+ preview,
+ schema,
+ },
+ collect: (monitor) => ({
+ isDragging: monitor.isDragging(),
+ opacity: monitor.isDragging() ? 0.9 : 1,
+ }),
+ end: (item, monitor) => {
+ const dropResult = monitor.getDropResult();
+ if (item && dropResult) {
+ if (dropResult.gridType === 'block') {
+ insertAfter(field.address.segments, dropResult.segments);
+ } else if (dropResult.gridType === 'row') {
+ insertAfterWithAddRow(field.address.segments, dropResult.segments);
+ } else if (dropResult.gridType === 'column') {
+ insertBeforeWithAddColumn(
+ field.address.segments,
+ dropResult.segments,
+ );
+ } else if (dropResult.gridType === 'last-column') {
+ appendToRowWithAddColumn(
+ field.address.segments,
+ dropResult.segments,
+ );
+ } else if (dropResult.gridType === 'first-row') {
+ insertBeforeWithAddRow(
+ field.address.segments,
+ dropResult.segments,
+ );
+ }
+ refresh();
+ }
+ },
+ }),
+ [field, schema],
+ );
+
+ const segments = field.address.segments;
+
+ const [{ canDrop, isOver }, drop] = useDrop(
+ () => ({
+ accept,
+ drop: () => {
+ console.log('source.segments', segments);
+ return { gridType: 'block', segments, schema };
+ },
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ canDrop: monitor.canDrop(),
+ }),
+ // hover: (item, monitor) => {
+ // console.log(monitor.getSourceClientOffset());
+ // },
+ canDrop: () => !isDragging,
+ }),
+ [isDragging, schema],
+ );
+
+ const active = canDrop && isOver;
+
+ drop(ref);
+
+ console.log({ isDragging });
+
+ return (
+
+
+ {children}
+ {/*
*/}
+
+ );
+};
+
+const ActionBar = ({ dragRef }) => {
+ const { addBlock, removeBlock } = useSchemaQuery();
+ const [active, setActive] = useState(false);
+ return (
+
+
+ {
+ addBlock({}, true);
+ setActive(false);
+ }}
+ icon={}
+ >
+ 在上方插入区块
+
+ {
+ addBlock({});
+ setActive(false);
+ }}
+ icon={}
+ >
+ 在下方插入区块
+
+
+ {
+ removeBlock();
+ setActive(false);
+ }}
+ icon={}
+ >
+ 删除区块
+
+
+ }
+ >
+
+
+
+ );
+};
+
+export default Grid;
diff --git a/packages/client/src/blocks/grid/demo.less b/packages/client/src/blocks/grid/demo.less
deleted file mode 100644
index 604a1c107..000000000
--- a/packages/client/src/blocks/grid/demo.less
+++ /dev/null
@@ -1,15 +0,0 @@
-#components-grid-demo [class~='ant-row'] {
- .ant-col {
- // background: rgba(128, 128, 128, 0.08);
- > div {
- padding: 16px;
- border-radius: 0;
- color: #fff;
- margin-bottom: 8px;
- margin-top: 8px;
- min-height: 30px;
- text-align: center;
- background: rgba(0,146,255,.75);
- }
- }
-}
\ No newline at end of file
diff --git a/packages/client/src/blocks/grid/demos/demo2.tsx b/packages/client/src/blocks/grid/demos/demo2.tsx
new file mode 100644
index 000000000..672d81bd0
--- /dev/null
+++ b/packages/client/src/blocks/grid/demos/demo2.tsx
@@ -0,0 +1,133 @@
+import React, { useMemo } from 'react';
+import { FormProvider, FormConsumer, useField, useFieldSchema } from '@formily/react';
+import { createForm } from '@formily/core';
+import {
+ SchemaFieldWithDesigner,
+ registerFieldComponents,
+ useSchema,
+} from '../../../fields';
+import { grid, row, column, block } from '../utils';
+import { blocks2properties } from '../utils';
+
+function Designer(props) {
+ const form = useMemo(() => createForm({}), []);
+ const { schema } = props;
+ return (
+
+
+
+ {/*
+ {(form) => {
+ return {JSON.stringify(form.values, null, 2)}
;
+ }}
+ */}
+
+
+ );
+}
+
+function Hello(props) {
+ const schema = useFieldSchema();
+ return (
+
+ Hello {schema.title}
+
+ );
+}
+
+registerFieldComponents({ Hello });
+const blocks = [
+ {
+ type: 'string',
+ title: `Block 1`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 2`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 2,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 3`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 2,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 4`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 1,
+ blockOrder: 2,
+ },
+ {
+ type: 'string',
+ title: `Block 5`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 3,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 6`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 3,
+ columnOrder: 2,
+ blockOrder: 1,
+ },
+];
+const schema = blocks2properties(blocks);
+
+export default () => {
+ console.log({schema});
+ return (
+
+
+ {/*
{JSON.stringify(schema, null, 2)}
*/}
+
+ );
+};
diff --git a/packages/client/src/blocks/grid/demos/demo3.tsx b/packages/client/src/blocks/grid/demos/demo3.tsx
new file mode 100644
index 000000000..f0ca50fdb
--- /dev/null
+++ b/packages/client/src/blocks/grid/demos/demo3.tsx
@@ -0,0 +1,200 @@
+import React, { useMemo } from 'react';
+import { FormProvider, FormConsumer, useField, useFieldSchema } from '@formily/react';
+import { createForm } from '@formily/core';
+import {
+ SchemaFieldWithDesigner,
+ registerFieldComponents,
+} from '../../../fields';
+import { grid, row, column, block } from '../utils';
+import { blocks2properties } from '../utils';
+import { Card } from 'antd';
+
+function Designer(props) {
+ const form = useMemo(() => createForm({}), []);
+ const { schema } = props;
+ return (
+
+
+
+ {/*
+ {(form) => {
+ return {JSON.stringify(form.values, null, 2)}
;
+ }}
+ */}
+
+
+ );
+}
+
+function Hello(props) {
+ const schema = useFieldSchema();
+ return (
+
+ Hello {schema.title}
+
+ );
+}
+
+registerFieldComponents({ Hello, Designer, Card });
+
+const nested = blocks2properties([
+ {
+ type: 'string',
+ title: `Nested Block 1`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Nested Block 2`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 2,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Nested Block 3`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 2,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+]);
+
+const blocks = [
+ {
+ type: 'string',
+ title: `Block 1`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 2`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 2,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 3`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 2,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 4`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 1,
+ columnOrder: 1,
+ blockOrder: 2,
+ },
+ {
+ type: 'string',
+ title: `Block 5`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 3,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 6`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 3,
+ columnOrder: 2,
+ blockOrder: 1,
+ },
+ {
+ type: 'string',
+ title: `Block 7`,
+ required: true,
+ 'x-read-pretty': false,
+ 'x-decorator': 'Card',
+ 'x-decorator-props': {
+ title: '内嵌区块(只能在当前区域内部拖拽)',
+ },
+ 'x-component': 'Designer',
+ 'x-component-props': {
+ schema: {
+ type: 'object',
+ properties: {
+ layout: {
+ type: 'void',
+ 'x-component': 'FormLayout',
+ 'x-component-props': {
+ layout: 'vertical',
+ },
+ properties: {
+ [nested.name]: nested,
+ },
+ },
+ },
+ },
+ },
+ rowOrder: 4,
+ columnOrder: 1,
+ blockOrder: 1,
+ },
+];
+const schema = blocks2properties(blocks);
+export default () => {
+ console.log({schema});
+ return (
+
+
+ {/*
{JSON.stringify(schema, null, 2)}
*/}
+
+ );
+};
diff --git a/packages/client/src/blocks/grid/index.md b/packages/client/src/blocks/grid/index.md
index 78d70549a..b1b070521 100644
--- a/packages/client/src/blocks/grid/index.md
+++ b/packages/client/src/blocks/grid/index.md
@@ -5,72 +5,56 @@ nav:
path: /client
group:
order: 2
- title: Blocks
+ title: Blocks
path: /client/blocks
---
# Grid - 栅格
-## 示例
+基于行(Row)和列(Col)来定义区块(Block)的外部框架。
-```tsx
-import React from 'react';
-import { BlockContext, GridBlock } from '@nocobase/client';
-import './demo.less';
+## 代码演示
-function Hello({ name }) {
- return Hello {name}
;
-}
+### 基本用法
-const blocks = [
- {
- name: 'gb1',
- 'x-component': 'Hello',
- 'x-row': 0,
- 'x-column': 0,
- 'x-sort': 1,
- },
- {
- name: 'gb2',
- 'x-component': 'Hello',
- 'x-row': 0,
- 'x-column': 0,
- 'x-sort': 2,
- },
- {
- name: 'gb3',
- 'x-component': 'Hello',
- 'x-row': 0,
- 'x-column': 1,
- 'x-sort': 0,
- },
- {
- name: 'gb4',
- 'x-component': 'Hello',
- 'x-row': 1,
- 'x-column': 0,
- 'x-sort': 0,
- },
- {
- name: 'gb5',
- 'x-component': 'Hello',
- 'x-row': 2,
- 'x-column': 0,
- 'x-sort': 0,
- },
-];
+
-export default () => {
- return (
-
-
-
-
-
- )
+### 内嵌区块
+
+
+
+## API 说明
+
+### Grid
+
+只能在同一个 Grid 里拖拽布局
+
+### Grid.Row
+
+行
+
+### Grid.Column
+
+列
+
+### Grid.Block
+
+区块
+
+### BlockOptions
+
+```ts
+interface BlockOptions {
+ rowOrder: number;
+ columnOrder: number;
+ blockOrder: number;
}
```
+
+- rowOrder:第几行
+- columnOrder:第几列
+- blockOrder:某单元格内部区块排序
+
+### blocks2properties
+
+原始 schema 需要至少 grid->row->col->block->custom 五层嵌套,写起来非常繁琐,`blocks2properties` 方法可以简化配置。
\ No newline at end of file
diff --git a/packages/client/src/blocks/grid/index.tsx b/packages/client/src/blocks/grid/index.tsx
index a07cd70ae..e69de29bb 100644
--- a/packages/client/src/blocks/grid/index.tsx
+++ b/packages/client/src/blocks/grid/index.tsx
@@ -1,49 +0,0 @@
-import React from 'react';
-import { Row, Col } from 'antd';
-import { useBlock } from '../';
-import set from 'lodash/set';
-
-export function BlockItem(props: any) {
- const { Component } = useBlock(props);
- if (!Component) {
- return null;
- }
- console.log('BlockItem')
- return
-}
-
-export interface GridBlockProps {
- blocks: any[];
-}
-
-export function GridBlock(props: GridBlockProps) {
- const { blocks = [] } = props;
- const obj = {
- rows: [],
- };
- blocks.forEach(block => {
- const path = ['rows', block['x-row'], block['x-column'], block['x-sort']];
- console.log(path);
- set(obj, path, block);
- });
- console.log({obj})
- return (
-
- {obj.rows.map((cols, rowIndex) => {
- return (
-
- {cols.map((items, colIndex) => {
- return (
-
- {items.map((item, key) => ) }
-
- )
- })}
-
- );
- })}
-
- );
-}
-
-export default GridBlock;
diff --git a/packages/client/src/blocks/grid/style.less b/packages/client/src/blocks/grid/style.less
new file mode 100644
index 000000000..5a2b5c602
--- /dev/null
+++ b/packages/client/src/blocks/grid/style.less
@@ -0,0 +1,143 @@
+.drop-column {
+ position: absolute;
+ top: 0;
+ left: -12px;
+ height: calc(100% - 24px);
+ width: 24px;
+ // border: 1px dashed rgb(232, 232, 232);
+ &.active {
+ &::after {
+ content: '';
+ display: block;
+ width: 12px;
+ height: 100%;
+ position: absolute;
+ left: 5px;
+ background: #e6f7ff;
+ }
+ }
+ &.last {
+ left: auto;
+ right: -24px;
+ }
+}
+.drop-block {
+ height: 12px;
+ position: absolute;
+ bottom: -12px;
+ width: 100%;
+ left: 0;
+ // background: #e6f7ff;
+ // pointer-events: none;
+ &.active {
+ background: #e6f7ff;
+ }
+}
+// .ant-formily-item + .drop-block {
+// position: absolute;
+// bottom: 12px;
+// width: 100%;
+// }
+.grid-row {
+ position: relative;
+
+ &.active {
+ &::after {
+ content: '';
+ display: block;
+ height: 12px;
+ position: absolute;
+ bottom: 5px;
+ width: 100%;
+ background: #e6f7ff;
+ }
+ }
+}
+
+.grid-block {
+ position: relative;
+ padding-bottom: 1px;
+ &.active {
+ &::after {
+ content: '';
+ display: block;
+ height: 12px;
+ position: absolute;
+ bottom: 5px;
+ width: 100%;
+ background: #e6f7ff;
+ }
+ }
+
+ &.grid-block--Input {
+ margin-bottom: 0;
+ .drop-block {
+ bottom: 0;
+ }
+ }rgb(172, 172, 172)
+ .grid-block-actions {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ z-index: 2;
+ display: none;
+ }
+ > .action-bar {
+ position: absolute;
+ right: 10px;
+ top: 5px;
+ z-index: 2;
+ display: none;
+ &.active {
+ display: inline-block;
+ }
+ }
+ &:hover,
+ &.hover {
+ > .action-bar {
+ display: inline-block;
+ }
+ }
+}
+
+.grid-block .ant-card {
+ margin-bottom: 24px;
+}
+
+.drop-row {
+ height: 12px;
+ // background: #e6f7ff;
+ position: absolute;
+ bottom: 5px;
+ width: 100%;
+ // background: #e6f7ff;
+ &.active {
+ background: #e6f7ff;
+ }
+
+ &.first {
+ top: -17px;
+ z-index: 2;
+ bottom: auto;
+ }
+}
+
+.draggable {
+ cursor: move !important; /* fallback: no `url()` support or images disabled */
+ cursor: -webkit-grab !important; /* Chrome 1-21, Safari 4+ */
+ cursor: -moz-grab !important; /* Firefox 1.5-26 */
+ cursor: grab !important; /* W3C standards syntax, should come least */
+}
+
+.draggable:active {
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ cursor: grabbing;
+}
+tr.drop-over-downward td {
+ border-bottom: 1px dashed #1890ff;
+}
+
+tr.drop-over-upward td {
+ border-top: 1px dashed #1890ff;
+}
\ No newline at end of file
diff --git a/packages/client/src/blocks/grid/utils.tsx b/packages/client/src/blocks/grid/utils.tsx
new file mode 100644
index 000000000..d2efc14db
--- /dev/null
+++ b/packages/client/src/blocks/grid/utils.tsx
@@ -0,0 +1,94 @@
+import set from 'lodash/set';
+import { uid } from '@formily/shared';
+
+export const grid = (...rows: any[]) => {
+ const rowProperties = {};
+ rows.forEach((row, index) => {
+ set(row, 'x-component-props.rowOrder', index);
+ rowProperties[row.name] = row;
+ });
+ const name = `g_${uid()}`;
+ return {
+ type: 'void',
+ name,
+ 'x-component': 'Grid',
+ 'x-component-props': {},
+ properties: rowProperties,
+ };
+};
+
+export const row = (...cols: any[]) => {
+ const rowName = `r_${uid()}`;
+ const colsProperties = {};
+ cols.forEach((col, index) => {
+ set(col, 'x-component-props.columnOrder', index);
+ set(col, 'x-component-props.span', 24 / cols.length);
+ colsProperties[col.name] = col;
+ });
+ return {
+ type: 'void',
+ name: rowName,
+ 'x-component': 'Grid.Row',
+ 'x-component-props': {},
+ properties: colsProperties,
+ };
+};
+
+export const column = (...blocks: any[]) => {
+ const colName = `c_${uid()}`;
+ const properties = {};
+ blocks.forEach((item) => {
+ properties[item.name] = item;
+ });
+ return {
+ name: colName,
+ type: 'void',
+ 'x-component': 'Grid.Column',
+ 'x-read-pretty': true,
+ 'x-component-props': {
+ labelCol: 6,
+ wrapperCol: 10,
+ span: 24,
+ },
+ properties,
+ };
+};
+
+export const block = (...fields: any[]) => {
+ const blockName = `b_${uid()}`;
+ const properties = {};
+ fields.forEach((item) => {
+ const name = item.name || `f_${uid()}`;
+ properties[name] = item;
+ });
+ const lastComponentType = fields[fields.length - 1]['x-component'];
+ return {
+ name: blockName,
+ type: 'void',
+ 'x-component': 'Grid.Block',
+ 'x-component-props': {
+ lastComponentType,
+ },
+ 'x-read-pretty': false,
+ properties,
+ };
+};
+
+export function blocks2properties(blocks: any[]) {
+ const obj = {
+ rows: [],
+ };
+ blocks.forEach(block => {
+ const path = ['rows', block['rowOrder'], block['columnOrder'], block['blockOrder']];
+ console.log(path);
+ set(obj, path, block);
+ });
+ return grid(...obj.rows.filter(Boolean).map((cols) => {
+ return row(
+ ...cols.filter(Boolean).map((items: any[]) => {
+ console.log({items: items.filter(Boolean)})
+ return column(...items.filter(Boolean).map(item => block(item)));
+ }),
+ );
+ }));
+}
diff --git a/packages/client/src/blocks/index.tsx b/packages/client/src/blocks/index.tsx
index ca3d11763..b101743e4 100644
--- a/packages/client/src/blocks/index.tsx
+++ b/packages/client/src/blocks/index.tsx
@@ -3,7 +3,6 @@ import React, { createContext, useContext } from 'react';
export * from './form';
export * from './descriptions';
export * from './table';
-export * from './grid';
export const BlockContext = createContext({});
diff --git a/packages/client/src/blocks/kanban/index.md b/packages/client/src/blocks/kanban/index.md
index dce2a6756..12d06e768 100644
--- a/packages/client/src/blocks/kanban/index.md
+++ b/packages/client/src/blocks/kanban/index.md
@@ -11,3 +11,9 @@ group:
# Kanban - 看板
+## 代码演示
+
+### 基本使用
+
+### 设计器模式
+
diff --git a/packages/client/src/blocks/table/index.md b/packages/client/src/blocks/table/index.md
index c0d9a03a2..4d8ab8abd 100644
--- a/packages/client/src/blocks/table/index.md
+++ b/packages/client/src/blocks/table/index.md
@@ -11,6 +11,11 @@ group:
# Table - 表格
-## 示例
+## 代码演示
+
+### 基本使用
+
+### 设计器模式
+
diff --git a/packages/client/src/components/MenuEditor/index.tsx b/packages/client/src/components/MenuEditor/index.tsx
index 563c09977..31869be5d 100644
--- a/packages/client/src/components/MenuEditor/index.tsx
+++ b/packages/client/src/components/MenuEditor/index.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
-import { Layout, Menu, Button, Dropdown } from 'antd';
+import { Layout, Menu, Button, Dropdown, Modal } from 'antd';
import {
AppstoreOutlined,
MenuOutlined,
@@ -132,7 +132,12 @@ export function AddNewAction(props) {
);
return (
- {props.children || }
+
+ {props.children || }
+
);
}
@@ -179,14 +184,26 @@ export function SettingAction(props) {
移动到
- remove()} key="delete">
+ {
+ Modal.confirm({
+ title: '确定菜单',
+ content: '确认删除此菜单项吗?',
+ onOk: remove,
+ });
+ }}
+ key="delete"
+ >
删除菜单
);
return (
-
+
);
diff --git a/packages/client/src/components/MenuEditor/style.less b/packages/client/src/components/MenuEditor/style.less
index 3f1c6220f..4a95d8451 100644
--- a/packages/client/src/components/MenuEditor/style.less
+++ b/packages/client/src/components/MenuEditor/style.less
@@ -41,4 +41,18 @@
display: inline-block;
}
}
+}
+
+.ant-menu-horizontal {
+ .menu-icons {
+ position: absolute;
+ right: 0;
+ background: #1890ff;
+ padding-left: 10px;
+ height: auto;
+ line-height: 1rem;
+ top: 50%;
+ transform: translateY(-50%);
+ transition: all 0.3s ease-in;
+ }
}
\ No newline at end of file
diff --git a/packages/client/src/components/RouteSwitch/index.tsx b/packages/client/src/components/RouteSwitch/index.tsx
index ae56e9eb7..81807d69e 100644
--- a/packages/client/src/components/RouteSwitch/index.tsx
+++ b/packages/client/src/components/RouteSwitch/index.tsx
@@ -19,7 +19,6 @@ export function RouteSwitch(props: RouteSwitchProps) {
{routes.map((route, key) => {
if (route.type === 'redirect') {
- console.log(route);
return (
Block
+ )
+}
+
+export const Block = connect(Test, mapProps(
+ (props, field) => {
+ // console.log({ props, field });
+ return {
+ ...props,
+ };
+ },
+));
+
+export default Block;
diff --git a/packages/client/src/fields/cascader/index.md b/packages/client/src/fields/cascader/index.md
index cbdbe9a1c..5382d1ad5 100644
--- a/packages/client/src/fields/cascader/index.md
+++ b/packages/client/src/fields/cascader/index.md
@@ -102,4 +102,16 @@ export default () => {
);
};
-```
\ No newline at end of file
+```
+
+## 参数说明
+
+### dataRequest vs dataSource
+
+- dataSource 静态选项,value 为 string 或 number。
+- dataRequest 动态选项,value 为 string 或者 number 时,需要异步获取 label;也可以是 object,无需异步获取数据。
+
+### 常用配置项
+
+- changeOnSelect:当此项为 true 时,点选每级菜单选项值都会发生变化
+- maxLevel:dataRequest 参数,省市区字段时可以用于配置最大选中级别
\ No newline at end of file
diff --git a/packages/client/src/fields/checkbox/index.tsx b/packages/client/src/fields/checkbox/index.tsx
index be0b8e4f7..43968c730 100644
--- a/packages/client/src/fields/checkbox/index.tsx
+++ b/packages/client/src/fields/checkbox/index.tsx
@@ -19,7 +19,7 @@ export const Checkbox: ComposedCheckbox = connect(
onInput: 'onChange',
},
(props, field) => {
- console.log({ props, field });
+ // console.log({ props, field });
return {
...props,
};
diff --git a/packages/client/src/fields/date-picker/index.md b/packages/client/src/fields/date-picker/index.md
index 12804b832..5bb04947b 100644
--- a/packages/client/src/fields/date-picker/index.md
+++ b/packages/client/src/fields/date-picker/index.md
@@ -112,3 +112,17 @@ export default () => {
);
};
```
+
+## 参数说明
+
+### dateFormat
+
+日期格式
+
+### showTime
+
+是否显示时间
+
+### timeFormat
+
+时间格式
diff --git a/packages/client/src/fields/index.tsx b/packages/client/src/fields/index.tsx
index d7ad569a0..f37907126 100644
--- a/packages/client/src/fields/index.tsx
+++ b/packages/client/src/fields/index.tsx
@@ -1,6 +1,15 @@
-import React, { useMemo } from 'react';
+import React, { createContext, useMemo, useState, useContext } from 'react';
import { Form, FormItem, FormLayout, Space } from '@formily/antd';
-import { FormProvider, createSchemaField } from '@formily/react';
+import {
+ FormProvider,
+ createSchemaField,
+ useField,
+ useFieldSchema,
+ Schema,
+ useForm,
+} from '@formily/react';
+import set from 'lodash/set';
+import { uid } from '@formily/shared';
import { createForm } from '@formily/core';
import * as core from '@formily/core';
import { Select } from './select';
@@ -17,6 +26,8 @@ import { Markdown } from './markdown';
import { Password } from './password';
import { Radio } from './radio';
import { TimePicker } from './time-picker';
+import { Block } from './block';
+import { Grid } from '../blocks/grid/Grid';
export function fields2properties(fields: any) {
const items = Array.isArray(fields) ? fields : [fields];
@@ -50,23 +61,26 @@ const fieldComponents = {
Radio,
Space,
TimePicker,
+ Block,
+ Grid,
Column: ({ children }) => children,
+ Blank: ({ children }) => children,
};
const fieldScope = {};
export function registerFieldComponents(components) {
- Object.keys(components).forEach(displayName => {
+ Object.keys(components).forEach((displayName) => {
fieldComponents[displayName] = components[displayName];
});
}
export function getFieldComponent(displayName: string) {
- return fieldComponents[displayName]
+ return fieldComponents[displayName];
}
export function registerFieldScope(scopes) {
- Object.keys(scopes).forEach(name => {
+ Object.keys(scopes).forEach((name) => {
fieldScope[name] = scopes[name];
});
}
@@ -76,6 +90,300 @@ export const SchemaField = createSchemaField({
scope: fieldScope,
});
+export const SchemaDesignerContext = createContext(new Schema({}));
+export const SchemaRefreshContext = createContext(null);
+
+export function SchemaFieldWithDesigner(props) {
+ function Container(props) {
+ const { schema } = props;
+ const [, refresh] = useState(0);
+ return (
+ {
+ refresh(Math.random());
+ }}
+ >
+
+
+
+
+ );
+ }
+ return ;
+}
+
+export const getFullPaths = (schema: Schema) => {
+ if (!schema) {
+ return [];
+ }
+ const paths = [schema.name];
+ if (schema.parent && schema.parent.name) {
+ paths.unshift(...getFullPaths(schema.parent));
+ }
+ return paths;
+};
+
+export function useSchema() {
+ const context = useContext(SchemaDesignerContext);
+ const refresh = useContext(SchemaRefreshContext);
+ const fieldSchema = useFieldSchema();
+ const paths = getFullPaths(fieldSchema);
+ let schema: Schema = context;
+ const names = [...paths];
+ while (names.length) {
+ schema = schema.properties[names.shift()];
+ }
+ return { schema, fieldSchema, refresh };
+}
+
+export function getSchema(context) {
+ return (paths) => {
+ const fullPaths = Array.isArray(paths) ? paths : getFullPaths(paths);
+ let s: Schema = context;
+ const names = [...fullPaths];
+ while (names.length) {
+ s = s.properties[names.shift()];
+ }
+ return s;
+ };
+}
+
+export function addPropertyBefore(target, prop) {
+ Object.keys(target.parent.properties).forEach((name) => {
+ if (name === target.name) {
+ target.parent.addProperty(prop.name, prop);
+ }
+ const property = target.parent.properties[name];
+ property.parent.removeProperty(property.name);
+ target.parent.addProperty(property.name, property.toJSON());
+ });
+}
+
+export function addPropertyAfter(target, prop) {
+ Object.keys(target.parent.properties).forEach((name) => {
+ const property = target.parent.properties[name];
+ property.parent.removeProperty(property.name);
+ target.parent.addProperty(property.name, property.toJSON());
+ if (name === target.name) {
+ target.parent.addProperty(prop.name, prop);
+ }
+ });
+}
+
+export function useSchemaQuery() {
+ const context = useContext(SchemaDesignerContext);
+ const form = useForm();
+ const { schema, refresh } = useSchema();
+ return {
+ removeBlock() {
+ schema.parent.removeProperty(schema.name);
+ refresh();
+ },
+ addBlock: (data, up = false) => {
+ const blockSchema = block({
+ type: 'string',
+ title: `Block ${uid()}`,
+ required: true,
+ 'x-read-pretty': false,
+ // 'x-decorator': 'FormItem',
+ 'x-component': 'Hello',
+ rowOrder: 2,
+ columnOrder: 1,
+ blockOrder: 1,
+ });
+ if ('Grid.Column' === schema.parent['x-component']) {
+ if (Object.keys(schema.parent.parent.properties).length > 1) {
+ up
+ ? addPropertyBefore(schema, blockSchema)
+ : addPropertyAfter(schema, blockSchema);
+ } else {
+ const rowSchema = row(column(blockSchema));
+ up
+ ? addPropertyAfter(schema.parent.parent, rowSchema)
+ : addPropertyAfter(schema.parent.parent, rowSchema);
+ }
+ }
+ console.log('x-component', schema.parent['x-component']);
+ refresh();
+ // schema.parent['x-component']
+ },
+ insertAfter: (sourcePath, targetPath) => {
+ const source = getSchema(context)(sourcePath);
+ const target = getSchema(context)(targetPath);
+ if (!source || !target) {
+ return;
+ }
+ console.log({
+ sourcePath,
+ source,
+ target,
+ targetPath,
+ sourceParentproperties: source.parent.properties,
+ });
+ const names = [];
+ Object.keys(target.parent.properties).forEach((name) => {
+ // if (names.includes(source.name)) {
+ // return;
+ // }
+ // names.push(name);
+ const property = target.parent.properties[name];
+ property.parent.removeProperty(property.name);
+ target.parent.addProperty(property.name, property.toJSON());
+ if (name === target.name) {
+ // names.push(source.name);
+ source.parent.removeProperty(source.name);
+ console.log('source.parent.properties', source.parent.properties);
+ target.parent.addProperty(source.name, source.toJSON());
+ }
+ });
+ },
+ insertAfterWithAddRow: (sourcePath, targetPath) => {
+ const source = getSchema(context)(sourcePath);
+ const target = getSchema(context)(targetPath);
+ const rowSchema = row(column(source.toJSON()));
+ source.parent.removeProperty(source.name);
+ Object.keys(target.parent.properties).forEach((name) => {
+ const property = target.parent.properties[name];
+ property.parent.removeProperty(property.name);
+ target.parent.addProperty(property.name, property.toJSON());
+ if (name === target.name) {
+ target.parent.addProperty(rowSchema.name, rowSchema);
+ }
+ });
+ },
+ insertBeforeWithAddRow: (sourcePath, targetPath) => {
+ const source = getSchema(context)(sourcePath);
+ const target = getSchema(context)(targetPath);
+ const rowSchema = row(column(source.toJSON()));
+ source.parent.removeProperty(source.name);
+ Object.keys(target.parent.properties).forEach((name) => {
+ if (name === target.name) {
+ target.parent.addProperty(rowSchema.name, rowSchema);
+ }
+ const property = target.parent.properties[name];
+ property.parent.removeProperty(property.name);
+ target.parent.addProperty(property.name, property.toJSON());
+ });
+ },
+ appendToRowWithAddColumn: (sourcePath, targetPath) => {
+ const source = getSchema(context)(sourcePath);
+ const target = getSchema(context)(targetPath);
+ const colSchema = column(source.toJSON());
+ source.parent.removeProperty(source.name);
+ const len = Object.keys(target.properties).length + 1;
+ target.addProperty(colSchema.name, colSchema);
+ console.log('target.properties', target.properties);
+ Object.keys(target.properties).forEach((name) => {
+ const prop = target.properties[name];
+ form.setFieldState(getFullPaths(prop).join('.'), (state) => {
+ state.componentProps = {
+ span: 24 / len,
+ };
+ });
+ });
+ },
+ insertBeforeWithAddColumn: (sourcePath, targetPath) => {
+ const source = getSchema(context)(sourcePath);
+ const target = getSchema(context)(targetPath);
+ const colSchema = column(source.toJSON());
+ source.parent.removeProperty(source.name);
+ const len = Object.keys(target.parent.properties).length + 1;
+ console.log('x-component-props.span', 24 / len);
+ Object.keys(target.parent.properties).forEach((name) => {
+ if (name === target.name) {
+ target.parent.addProperty(colSchema.name, colSchema);
+ }
+ const property = target.parent.properties[name];
+ property.parent.removeProperty(property.name);
+ const json = property.toJSON();
+ target.parent.addProperty(property.name, json);
+ });
+ Object.keys(target.parent.properties).forEach((name) => {
+ const prop = target.parent.properties[name];
+ form.setFieldState(getFullPaths(prop).join('.'), (state) => {
+ state.componentProps = {
+ span: 24 / len,
+ };
+ });
+ });
+ },
+ };
+}
+
+export const grid = (...rows: any[]) => {
+ const rowProperties = {};
+ rows.forEach((row, index) => {
+ set(row, 'x-component-props.rowOrder', index);
+ rowProperties[row.name] = row;
+ });
+ const name = `g_${uid()}`;
+ return {
+ type: 'void',
+ name,
+ 'x-component': 'Grid',
+ 'x-component-props': {},
+ properties: rowProperties,
+ };
+};
+
+export const row = (...cols: any[]) => {
+ const rowName = `r_${uid()}`;
+ const colsProperties = {};
+ cols.forEach((col, index) => {
+ set(col, 'x-component-props.columnOrder', index);
+ set(col, 'x-component-props.span', 24 / cols.length);
+ colsProperties[col.name] = col;
+ });
+ return {
+ type: 'void',
+ name: rowName,
+ 'x-component': 'Grid.Row',
+ 'x-component-props': {},
+ properties: colsProperties,
+ };
+};
+
+export const column = (...blocks: any[]) => {
+ const colName = `c_${uid()}`;
+ const properties = {};
+ blocks.forEach((item) => {
+ properties[item.name] = item;
+ });
+ return {
+ name: colName,
+ type: 'void',
+ 'x-component': 'Grid.Column',
+ 'x-read-pretty': true,
+ 'x-component-props': {
+ labelCol: 6,
+ wrapperCol: 10,
+ span: 24,
+ },
+ properties,
+ };
+};
+
+export const block = (...fields: any[]) => {
+ const blockName = `b_${uid()}`;
+ const properties = {};
+ fields.forEach((item) => {
+ const name = item.name || `f_${uid()}`;
+ // item.title = `${item.title} ${name}`;
+ properties[name] = item;
+ });
+ const lastComponentType = fields[fields.length - 1]['x-component'];
+ return {
+ name: blockName,
+ type: 'void',
+ 'x-component': 'Grid.Block',
+ 'x-component-props': {
+ lastComponentType,
+ },
+ 'x-read-pretty': false,
+ properties,
+ };
+};
+
export function parseEffects(effects: any, form?: any) {
if (!effects) {
return;
@@ -102,15 +410,17 @@ export interface FieldProps {
export function Field(props: FieldProps) {
const { schema, readPretty, data, effects } = props;
- const form = props.form || useMemo(
- () =>
- createForm({
- initialValues: data,
- readPretty,
- effects: (form) => parseEffects(effects, form),
- }),
- [],
- );
+ const form =
+ props.form ||
+ useMemo(
+ () =>
+ createForm({
+ initialValues: data,
+ readPretty,
+ effects: (form) => parseEffects(effects, form),
+ }),
+ [],
+ );
return (
diff --git a/packages/client/src/fields/input/index.md b/packages/client/src/fields/input/index.md
index e30c63e14..e7f62ef8b 100644
--- a/packages/client/src/fields/input/index.md
+++ b/packages/client/src/fields/input/index.md
@@ -280,4 +280,15 @@ export default () => {
## 参数说明
-输入框组件暂时还没有特殊的参数,其他通用参数参考 [Field 章节](fields)。
\ No newline at end of file
+输入框组件暂时还没有特殊的参数,其他通用参数参考 [Field 章节](fields)。
+
+常用配置参数:
+
+- type
+- title
+- name
+- required
+- default
+- x-component-props.placeholder
+- x-decorator-props.description
+- x-validator
diff --git a/packages/client/src/templates/admin-layout/index.tsx b/packages/client/src/templates/admin-layout/index.tsx
index 9877eea1b..17401af96 100644
--- a/packages/client/src/templates/admin-layout/index.tsx
+++ b/packages/client/src/templates/admin-layout/index.tsx
@@ -38,7 +38,7 @@ function Menu() {
{data.map((item, index) => (
-
- {item.title}
+ {item.title}
))}
diff --git a/packages/client/src/templates/page-template/index.tsx b/packages/client/src/templates/page-template/index.tsx
index 258454e60..6bd36a525 100644
--- a/packages/client/src/templates/page-template/index.tsx
+++ b/packages/client/src/templates/page-template/index.tsx
@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from 'react';
import { Spin } from 'antd';
import { Helmet } from 'react-helmet';
import { usePage, BlockContext } from '../../';
+import { useRequest } from 'ahooks';
export function PageTemplate({ route }) {
const { GridBlock } = useContext(BlockContext);
@@ -9,11 +10,14 @@ export function PageTemplate({ route }) {
useEffect(() => {
setPreName(route.name);
}, [route.name]);
- const { data = {}, loading } = usePage(route.name);
+ const { data = {}, loading } = useRequest(`/api/routes:getPage?name=${route.name}`, {
+ // refreshDeps: [route.name],
+ });
+ console.log(data.title, { loading, preName, blocks: data.blocks });
if (loading || preName !== route.name) {
return ;
}
- console.log(data.title, { preName, blocks: data.blocks });
+ console.log(data.title, { loading, preName, blocks: data.blocks });
return (