updates
This commit is contained in:
parent
e8399e167e
commit
8d298de6fe
@ -17,13 +17,39 @@ import {
|
|||||||
useHistory,
|
useHistory,
|
||||||
Redirect,
|
Redirect,
|
||||||
} from 'react-router-dom';
|
} 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 { useRequest } from 'ahooks';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import Database from './datatable';
|
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 (
|
||||||
|
<Button
|
||||||
|
className={cls('nb-designable-toggle', { active: designable })}
|
||||||
|
type={'primary'}
|
||||||
|
style={{ height: 46, border: 0 }}
|
||||||
|
onClick={() => {
|
||||||
|
setDesignable(!designable);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HighlightOutlined />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function LayoutWithMenu({ schema }) {
|
function LayoutWithMenu({ schema }) {
|
||||||
const match = useRouteMatch<any>();
|
const match = useRouteMatch<any>();
|
||||||
@ -58,6 +84,7 @@ function LayoutWithMenu({ schema }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Database />
|
<Database />
|
||||||
|
<DesignableToggle />
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Sider
|
<Layout.Sider
|
||||||
@ -103,11 +130,13 @@ export function AdminLayout({ route }: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SwithDesignableContextProvider>
|
||||||
<CollectionContextProvider>
|
<CollectionContextProvider>
|
||||||
<PageTitleContextProvider>
|
<PageTitleContextProvider>
|
||||||
<LayoutWithMenu schema={data} />
|
<LayoutWithMenu schema={data} />
|
||||||
</PageTitleContextProvider>
|
</PageTitleContextProvider>
|
||||||
</CollectionContextProvider>
|
</CollectionContextProvider>
|
||||||
|
</SwithDesignableContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,3 +47,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nb-designable-toggle {
|
||||||
|
background: none !important;
|
||||||
|
&.active {
|
||||||
|
background: #1890ff !important;
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ import { useMemo } from 'react';
|
|||||||
import { CodeOutlined } from '@ant-design/icons';
|
import { CodeOutlined } from '@ant-design/icons';
|
||||||
import Editor from '@monaco-editor/react';
|
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 { Space, Card, Modal, Spin } from 'antd';
|
||||||
import { Action } from '../../schemas/action';
|
import { Action } from '../../schemas/action';
|
||||||
@ -50,6 +50,7 @@ import { CardItem } from '../../schemas/card-item';
|
|||||||
import { DragAndDrop } from '../../schemas/drag-and-drop';
|
import { DragAndDrop } from '../../schemas/drag-and-drop';
|
||||||
import { TreeSelect } from '../../schemas/tree-select';
|
import { TreeSelect } from '../../schemas/tree-select';
|
||||||
import { Page } from '../../schemas/page';
|
import { Page } from '../../schemas/page';
|
||||||
|
import { useCollectionContext, useSwithDesignableContext } from '../../schemas';
|
||||||
|
|
||||||
export const BlockContext = createContext({ dragRef: null });
|
export const BlockContext = createContext({ dragRef: null });
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ export const SchemaField = createSchemaField({
|
|||||||
ArrayTable,
|
ArrayTable,
|
||||||
FormLayout,
|
FormLayout,
|
||||||
TreeSelect,
|
TreeSelect,
|
||||||
|
FormilyFormItem,
|
||||||
|
|
||||||
DragAndDrop,
|
DragAndDrop,
|
||||||
|
|
||||||
@ -188,10 +190,12 @@ export function useSchemaComponent(component: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useDesignable(path?: any) {
|
export function useDesignable(path?: any) {
|
||||||
|
const { designable } = useSwithDesignableContext();
|
||||||
const { schema = new Schema({}), refresh } = useContext(DesignableContext);
|
const { schema = new Schema({}), refresh } = useContext(DesignableContext);
|
||||||
const schemaPath = path || useSchemaPath();
|
const schemaPath = path || useSchemaPath();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
let currentSchema =
|
let current;
|
||||||
|
let currentSchema = current =
|
||||||
findPropertyByPath(schema, schemaPath) || ({} as Schema);
|
findPropertyByPath(schema, schemaPath) || ({} as Schema);
|
||||||
if (!currentSchema) {
|
if (!currentSchema) {
|
||||||
currentSchema = fieldSchema;
|
currentSchema = fieldSchema;
|
||||||
@ -204,6 +208,9 @@ export function useDesignable(path?: any) {
|
|||||||
const DesignableBar =
|
const DesignableBar =
|
||||||
get(options.components, currentSchema['x-designable-bar']) || (() => null);
|
get(options.components, currentSchema['x-designable-bar']) || (() => null);
|
||||||
return {
|
return {
|
||||||
|
designable,
|
||||||
|
root: schema,
|
||||||
|
currentSchema: current,
|
||||||
DesignableBar,
|
DesignableBar,
|
||||||
schema: currentSchema,
|
schema: currentSchema,
|
||||||
refresh,
|
refresh,
|
||||||
@ -325,10 +332,14 @@ export function useDesignable(path?: any) {
|
|||||||
}
|
}
|
||||||
s.parent.removeProperty(s.name);
|
s.parent.removeProperty(s.name);
|
||||||
removed.push(s);
|
removed.push(s);
|
||||||
|
if (s['x-component'] === 'Grid.Row') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Object.keys(s.parent.properties || {}).length === 0) {
|
if (Object.keys(s.parent.properties || {}).length === 0) {
|
||||||
remove(s.parent);
|
remove(s.parent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
console.log({ removed })
|
||||||
remove(target);
|
remove(target);
|
||||||
refresh();
|
refresh();
|
||||||
return removed;
|
return removed;
|
||||||
@ -353,12 +364,12 @@ export function getSchemaPath(schema: Schema) {
|
|||||||
const path = schema['x-path'] || [schema.name];
|
const path = schema['x-path'] || [schema.name];
|
||||||
let parent = schema.parent;
|
let parent = schema.parent;
|
||||||
while (parent) {
|
while (parent) {
|
||||||
if (!parent.name) {
|
// if (!parent.name) {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
if (parent['x-path']) {
|
if (parent['x-path']) {
|
||||||
path.unshift(...parent['x-path']);
|
path.unshift(...parent['x-path']);
|
||||||
} else {
|
} else if (parent.name) {
|
||||||
path.unshift(parent.name);
|
path.unshift(parent.name);
|
||||||
}
|
}
|
||||||
parent = parent.parent;
|
parent = parent.parent;
|
||||||
@ -434,6 +445,7 @@ export interface SchemaRendererProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SchemaRenderer = (props: SchemaRendererProps) => {
|
export const SchemaRenderer = (props: SchemaRendererProps) => {
|
||||||
|
const { designable } = useSwithDesignableContext();
|
||||||
const [, refresh] = useState(uid());
|
const [, refresh] = useState(uid());
|
||||||
const form = useMemo(() => props.form || createForm(), []);
|
const form = useMemo(() => props.form || createForm(), []);
|
||||||
const schema = useMemo(() => {
|
const schema = useMemo(() => {
|
||||||
@ -456,6 +468,10 @@ export const SchemaRenderer = (props: SchemaRendererProps) => {
|
|||||||
}
|
}
|
||||||
return new Schema(s);
|
return new Schema(s);
|
||||||
}, []);
|
}, []);
|
||||||
|
// useEffect(() => {
|
||||||
|
// refresh(uid());
|
||||||
|
// }, [designable]);
|
||||||
|
console.log({ designable }, 'designable')
|
||||||
const defaultRender = ({ schema }) => (
|
const defaultRender = ({ schema }) => (
|
||||||
<FormProvider form={form}>
|
<FormProvider form={form}>
|
||||||
<SchemaField
|
<SchemaField
|
||||||
|
@ -19,6 +19,8 @@ const request = extend({
|
|||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// console.log = () => {}
|
||||||
|
|
||||||
const RouteSwitch = createRouteSwitch({
|
const RouteSwitch = createRouteSwitch({
|
||||||
components: {
|
components: {
|
||||||
AdminLayout,
|
AdminLayout,
|
||||||
|
@ -30,7 +30,7 @@ export const Action: any = observer((props: any) => {
|
|||||||
const isDropdownOrPopover =
|
const isDropdownOrPopover =
|
||||||
child &&
|
child &&
|
||||||
['Action.Dropdown', 'Action.Popover'].includes(child['x-component']);
|
['Action.Dropdown', 'Action.Popover'].includes(child['x-component']);
|
||||||
console.log({ DesignableBar, schema });
|
// console.log({ DesignableBar, schema });
|
||||||
return (
|
return (
|
||||||
<VisibleContext.Provider value={[visible, setVisible]}>
|
<VisibleContext.Provider value={[visible, setVisible]}>
|
||||||
{!isDropdownOrPopover && (
|
{!isDropdownOrPopover && (
|
||||||
|
@ -47,6 +47,7 @@ import {
|
|||||||
DatabaseOutlined,
|
DatabaseOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
|
createCollectionField,
|
||||||
createOrUpdateCollection,
|
createOrUpdateCollection,
|
||||||
createSchema,
|
createSchema,
|
||||||
request,
|
request,
|
||||||
@ -57,6 +58,9 @@ import {
|
|||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { SchemaField } from '../../components/schema-renderer';
|
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 generateGridBlock = (schema: ISchema) => {
|
||||||
const name = schema.name || uid();
|
const name = schema.name || uid();
|
||||||
@ -429,6 +433,9 @@ function generateCardItemSchema(component) {
|
|||||||
name: uid(),
|
name: uid(),
|
||||||
'x-decorator': 'CardItem',
|
'x-decorator': 'CardItem',
|
||||||
'x-component': 'Form',
|
'x-component': 'Form',
|
||||||
|
'x-component-props': {
|
||||||
|
showDefaultButtons: true,
|
||||||
|
},
|
||||||
'x-designable-bar': 'Form.DesignableBar',
|
'x-designable-bar': 'Form.DesignableBar',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
@ -491,9 +498,14 @@ AddNew.CardItem = observer((props: any) => {
|
|||||||
const { ghost, defaultAction } = props;
|
const { ghost, defaultAction } = props;
|
||||||
const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
|
const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
|
||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
const { data: collections = [], loading, run } = useRequest('collections:findAll', {
|
// const {
|
||||||
formatResult: (result) => result?.data,
|
// data: collections = [],
|
||||||
});
|
// loading,
|
||||||
|
// run,
|
||||||
|
// } = useRequest('collections:findAll', {
|
||||||
|
// formatResult: (result) => result?.data,
|
||||||
|
// });
|
||||||
|
const { data: collections = [], loading, refresh } = useCollectionContext();
|
||||||
console.log({ collections });
|
console.log({ collections });
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@ -501,9 +513,6 @@ AddNew.CardItem = observer((props: any) => {
|
|||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
}}
|
}}
|
||||||
onVisibleChange={(visible) => {
|
onVisibleChange={(visible) => {
|
||||||
if (visible) {
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
console.log('onVisibleChange', visible);
|
console.log('onVisibleChange', visible);
|
||||||
}}
|
}}
|
||||||
placement={'bottomCenter'}
|
placement={'bottomCenter'}
|
||||||
@ -511,6 +520,7 @@ AddNew.CardItem = observer((props: any) => {
|
|||||||
<Menu
|
<Menu
|
||||||
onClick={async (info) => {
|
onClick={async (info) => {
|
||||||
let data: ISchema;
|
let data: ISchema;
|
||||||
|
let collectionName = null;
|
||||||
if (['addNewTable', 'addNewForm'].includes(info.key)) {
|
if (['addNewTable', 'addNewForm'].includes(info.key)) {
|
||||||
const values = await FormDialog(`新建数据表`, () => {
|
const values = await FormDialog(`新建数据表`, () => {
|
||||||
return (
|
return (
|
||||||
@ -525,13 +535,16 @@ AddNew.CardItem = observer((props: any) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await createOrUpdateCollection(values);
|
await createOrUpdateCollection(values);
|
||||||
|
refresh();
|
||||||
data = generateCardItemSchema(
|
data = generateCardItemSchema(
|
||||||
info.key === 'addNewTable' ? 'Table' : 'Form',
|
info.key === 'addNewTable' ? 'Table' : 'Form',
|
||||||
);
|
);
|
||||||
|
collectionName = values.name;
|
||||||
} else if (info.key !== 'Markdown') {
|
} else if (info.key !== 'Markdown') {
|
||||||
const keys = info.key.split('.');
|
const keys = info.key.split('.');
|
||||||
const component = keys.shift();
|
const component = keys.shift();
|
||||||
const tableName = keys.join('.');
|
const tableName = keys.join('.');
|
||||||
|
collectionName = tableName;
|
||||||
data = generateCardItemSchema(component);
|
data = generateCardItemSchema(component);
|
||||||
console.log('info.keyPath', component, tableName);
|
console.log('info.keyPath', component, tableName);
|
||||||
} else {
|
} else {
|
||||||
@ -540,6 +553,10 @@ AddNew.CardItem = observer((props: any) => {
|
|||||||
if (schema['key']) {
|
if (schema['key']) {
|
||||||
data['key'] = uid();
|
data['key'] = uid();
|
||||||
}
|
}
|
||||||
|
if (collectionName) {
|
||||||
|
data['x-component-props'] = data['x-component-props'] || {};
|
||||||
|
data['x-component-props']['collectionName'] = collectionName;
|
||||||
|
}
|
||||||
if (isGridBlock(schema)) {
|
if (isGridBlock(schema)) {
|
||||||
path.pop();
|
path.pop();
|
||||||
path.pop();
|
path.pop();
|
||||||
@ -669,8 +686,9 @@ AddNew.BlockItem = observer((props: any) => {
|
|||||||
|
|
||||||
AddNew.FormItem = observer((props: any) => {
|
AddNew.FormItem = observer((props: any) => {
|
||||||
const { ghost, defaultAction } = props;
|
const { ghost, defaultAction } = props;
|
||||||
const { schema, insertBefore, insertAfter } = useDesignable();
|
const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
|
||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
|
const { collection = {}, refresh } = useFormContext();
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
overlayStyle={{
|
overlayStyle={{
|
||||||
@ -679,24 +697,33 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
placement={'bottomCenter'}
|
placement={'bottomCenter'}
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.ItemGroup title={'选择已有字段'}>
|
<Menu.ItemGroup title={`选择「${collection.title}」里已有字段`}>
|
||||||
|
{collection?.fields?.map((field) => (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
style={{
|
key={field.key}
|
||||||
minWidth: 150,
|
|
||||||
}}
|
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
let data: ISchema = generateFormItemSchema('Markdown');
|
let data: ISchema = {
|
||||||
if (schema['key']) {
|
key: uid(),
|
||||||
data['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)) {
|
if (isGridBlock(schema)) {
|
||||||
path.pop();
|
path.pop();
|
||||||
path.pop();
|
path.pop();
|
||||||
data = generateGridBlock(data);
|
data = generateGridBlock(data);
|
||||||
|
} else if (isGrid(schema)) {
|
||||||
|
data = generateGridBlock(data);
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
let s;
|
let s;
|
||||||
if (defaultAction === 'insertAfter') {
|
if (isGrid(schema)) {
|
||||||
|
s = appendChild(data, [...path]);
|
||||||
|
} else if (defaultAction === 'insertAfter') {
|
||||||
s = insertAfter(data, [...path]);
|
s = insertAfter(data, [...path]);
|
||||||
} else {
|
} else {
|
||||||
s = insertBefore(data, [...path]);
|
s = insertBefore(data, [...path]);
|
||||||
@ -705,41 +732,101 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Checkbox /> 字段 1
|
{field?.uiSchema?.title || '未命名'}
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item>
|
|
||||||
<Checkbox /> 字段2
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item>
|
|
||||||
<Checkbox /> 字段3
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item>
|
|
||||||
<Checkbox /> 字段4
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item>
|
|
||||||
<Checkbox /> 字段5
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item>
|
|
||||||
<Checkbox /> 字段6
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item>
|
|
||||||
<Checkbox /> 字段7
|
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
))}
|
||||||
</Menu.ItemGroup>
|
</Menu.ItemGroup>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.SubMenu title={'新增字段'}>
|
<Menu.SubMenu title={'新建字段'}>
|
||||||
|
{options.map((option) => (
|
||||||
|
<Menu.ItemGroup title={option.label}>
|
||||||
|
{option.children.map((item) => (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
style={{
|
style={{ minWidth: 150 }}
|
||||||
minWidth: 150,
|
key={item.name}
|
||||||
|
onClick={async () => {
|
||||||
|
const values = await FormDialog(`新增字段`, () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField schema={item} />
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).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}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item>多行文本</Menu.Item>
|
))}
|
||||||
<Menu.Item>手机号</Menu.Item>
|
</Menu.ItemGroup>
|
||||||
<Menu.Item>数字</Menu.Item>
|
))}
|
||||||
<Menu.Item>说明文本</Menu.Item>
|
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
onClick={async () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
添加说明文字
|
||||||
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -39,7 +39,7 @@ const DraggableBlock = (props) => {
|
|||||||
<div
|
<div
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
setActive(true);
|
setActive(true);
|
||||||
console.log('e.onMouseEnter', new Date().toString());
|
// console.log('e.onMouseEnter', new Date().toString());
|
||||||
}}
|
}}
|
||||||
onMouseMove={(event) => {
|
onMouseMove={(event) => {
|
||||||
let dropElement = document.elementFromPoint(
|
let dropElement = document.elementFromPoint(
|
||||||
@ -65,11 +65,11 @@ const DraggableBlock = (props) => {
|
|||||||
if (dropIds.length > 0) {
|
if (dropIds.length > 0) {
|
||||||
setActive(dropIds[0] === schema.name);
|
setActive(dropIds[0] === schema.name);
|
||||||
}
|
}
|
||||||
console.log('e.onMouseMove', dropIds, schema.name);
|
// console.log('e.onMouseMove', dropIds, schema.name);
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
setActive(false);
|
setActive(false);
|
||||||
console.log('e.onMouseLeave', new Date().toString());
|
// console.log('e.onMouseLeave', new Date().toString());
|
||||||
}}
|
}}
|
||||||
ref={mergeRefs([previewRef, dropRef])}
|
ref={mergeRefs([previewRef, dropRef])}
|
||||||
className={cls('nb-grid-block', 'designable-form-item', {
|
className={cls('nb-grid-block', 'designable-form-item', {
|
||||||
|
@ -6,12 +6,15 @@ export const select: ISchema = {
|
|||||||
title: '下拉选择',
|
title: '下拉选择',
|
||||||
group: 'choices',
|
group: 'choices',
|
||||||
default: {
|
default: {
|
||||||
|
interface: 'select',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
// name,
|
// name,
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
// title,
|
// title,
|
||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
enum: [],
|
enum: [],
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
},
|
},
|
||||||
|
@ -6,12 +6,15 @@ export const string: ISchema = {
|
|||||||
title: '单行文本',
|
title: '单行文本',
|
||||||
group: 'basic',
|
group: 'basic',
|
||||||
default: {
|
default: {
|
||||||
|
interface: 'string',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
// name,
|
// name,
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
// title,
|
// title,
|
||||||
'x-component': 'Input',
|
'x-component': 'Input',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-designable-bar': 'Input.DesignableBar',
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -6,11 +6,14 @@ export const subTable: ISchema = {
|
|||||||
title: '子表格',
|
title: '子表格',
|
||||||
group: 'relation',
|
group: 'relation',
|
||||||
default: {
|
default: {
|
||||||
|
interface: 'subTable',
|
||||||
// name,
|
// name,
|
||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
// title,
|
// title,
|
||||||
'x-component': 'Select',
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Table',
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
enum: [],
|
enum: [],
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,9 @@ export const textarea: ISchema = {
|
|||||||
uiSchema: {
|
uiSchema: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
// title,
|
// title,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Input.TextArea',
|
'x-component': 'Input.TextArea',
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
|
94
packages/client/src/schemas/form/DesignableBar.tsx
Normal file
94
packages/client/src/schemas/form/DesignableBar.tsx
Normal file
@ -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 (
|
||||||
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Space size={'small'}>
|
||||||
|
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||||
|
{dragRef && <DragOutlined ref={dragRef} />}
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item
|
||||||
|
key={'update'}
|
||||||
|
onClick={() => {
|
||||||
|
field.readPretty = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑表单配置
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
key={'delete'}
|
||||||
|
onClick={async () => {
|
||||||
|
const removed = deepRemove();
|
||||||
|
// console.log({ removed })
|
||||||
|
const last = removed.pop();
|
||||||
|
if (isGridRowOrCol(last)) {
|
||||||
|
await removeSchema(last);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除当前表单
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
114
packages/client/src/schemas/form/Field.DesignableBar.tsx
Normal file
114
packages/client/src/schemas/form/Field.DesignableBar.tsx
Normal file
@ -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 (
|
||||||
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Space size={'small'}>
|
||||||
|
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||||
|
{dragRef && <DragOutlined ref={dragRef} />}
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item
|
||||||
|
key={'update'}
|
||||||
|
onClick={async () => {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改字段配置
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
key={'delete'}
|
||||||
|
onClick={async () => {
|
||||||
|
const removed = deepRemove();
|
||||||
|
console.log({ schema, removed });
|
||||||
|
const last = removed.pop();
|
||||||
|
if (isGridRowOrCol(last)) {
|
||||||
|
await removeSchema(last);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除当前字段
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -20,6 +20,13 @@ group:
|
|||||||
// 添加其他节点
|
// 添加其他节点
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
|
<Form>
|
||||||
|
// Form.Field 可用于覆盖字段原配置,但不修改原字段配置,新配置只作用于当前 Form
|
||||||
|
<Form.Field title={'自定义标题'}>
|
||||||
|
<Input title={'原标题'}>
|
||||||
|
</Form.Field>
|
||||||
|
</Form>
|
||||||
|
|
||||||
// Form 用作 decorator 时只用于创建独立的作用域
|
// Form 用作 decorator 时只用于创建独立的作用域
|
||||||
<Table x-decorator={'Form'}/>
|
<Table x-decorator={'Form'}/>
|
||||||
</pre>
|
</pre>
|
||||||
|
@ -9,10 +9,18 @@ import {
|
|||||||
FormProvider,
|
FormProvider,
|
||||||
ISchema,
|
ISchema,
|
||||||
useField,
|
useField,
|
||||||
|
useForm,
|
||||||
|
RecursionField,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../';
|
import {
|
||||||
|
useSchemaPath,
|
||||||
|
SchemaField,
|
||||||
|
useDesignable,
|
||||||
|
removeSchema,
|
||||||
|
useCollectionContext,
|
||||||
|
} from '../';
|
||||||
import get from 'lodash/get';
|
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 { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { FormLayout } from '@formily/antd';
|
import { FormLayout } from '@formily/antd';
|
||||||
@ -20,9 +28,34 @@ import './style.less';
|
|||||||
import AddNew from '../add-new';
|
import AddNew from '../add-new';
|
||||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||||
import { isGridRowOrCol } from '../grid';
|
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) => {
|
export const Form: any = observer((props: any) => {
|
||||||
const { useValues = () => ({}), ...others } = props;
|
const {
|
||||||
|
useValues = () => ({}),
|
||||||
|
showDefaultButtons = false,
|
||||||
|
...others
|
||||||
|
} = props;
|
||||||
const initialValues = useValues();
|
const initialValues = useValues();
|
||||||
const form = useMemo(() => {
|
const form = useMemo(() => {
|
||||||
console.log('Form.useMemo', initialValues);
|
console.log('Form.useMemo', initialValues);
|
||||||
@ -33,6 +66,8 @@ export const Form: any = observer((props: any) => {
|
|||||||
const scope = useContext(SchemaExpressionScopeContext);
|
const scope = useContext(SchemaExpressionScopeContext);
|
||||||
return (
|
return (
|
||||||
<FormProvider form={form}>
|
<FormProvider form={form}>
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
<FormContextProvider schema={schema} form={form}>
|
||||||
{schema['x-decorator'] === 'Form' ? (
|
{schema['x-decorator'] === 'Form' ? (
|
||||||
<SchemaField
|
<SchemaField
|
||||||
scope={scope}
|
scope={scope}
|
||||||
@ -59,65 +94,77 @@ export const Form: any = observer((props: any) => {
|
|||||||
/>
|
/>
|
||||||
</FormLayout>
|
</FormLayout>
|
||||||
)}
|
)}
|
||||||
|
{showDefaultButtons && (
|
||||||
|
<Space style={{ marginTop: 24 }}>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const values = await form.submit();
|
||||||
|
console.log({ values });
|
||||||
|
}}
|
||||||
|
type={'primary'}
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
await form.reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
</FormContextProvider>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Form.__Decorator = ({ children }) => children;
|
export const FormFieldUIDContext = createContext(null);
|
||||||
|
|
||||||
Form.DesignableBar = observer((props) => {
|
Form.Field = observer((props: any) => {
|
||||||
const field = useField();
|
const { fieldName } = props;
|
||||||
const { schema, refresh, deepRemove } = useDesignable();
|
const { schema } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const path = getSchemaPath(schema);
|
||||||
const { dragRef } = useContext(DraggableBlockContext);
|
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 (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<RecursionField
|
||||||
<span
|
name={name}
|
||||||
onClick={(e) => {
|
schema={
|
||||||
e.stopPropagation();
|
new Schema({
|
||||||
}}
|
'x-path': path,
|
||||||
className={cls('designable-bar-actions', { active: visible })}
|
type: 'void',
|
||||||
>
|
properties: {
|
||||||
<Space size={'small'}>
|
[field.name]: {
|
||||||
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
...field.uiSchema,
|
||||||
{dragRef && <DragOutlined ref={dragRef} />}
|
title,
|
||||||
<Dropdown
|
'x-decorator': 'FormilyFormItem',
|
||||||
trigger={['click']}
|
},
|
||||||
visible={visible}
|
},
|
||||||
onVisibleChange={(visible) => {
|
})
|
||||||
setVisible(visible);
|
|
||||||
}}
|
|
||||||
overlay={
|
|
||||||
<Menu>
|
|
||||||
<Menu.Item
|
|
||||||
key={'update'}
|
|
||||||
onClick={() => {
|
|
||||||
field.readPretty = false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
编辑Markdown
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Divider />
|
|
||||||
<Menu.Item
|
|
||||||
key={'delete'}
|
|
||||||
onClick={async () => {
|
|
||||||
const removed = deepRemove();
|
|
||||||
// console.log({ removed })
|
|
||||||
const last = removed.pop();
|
|
||||||
if (isGridRowOrCol(last)) {
|
|
||||||
await removeSchema(last);
|
|
||||||
}
|
}
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
删除当前区块
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<MenuOutlined />
|
|
||||||
</Dropdown>
|
|
||||||
</Space>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Form.Field.Item = observer((props) => {
|
||||||
|
const name = uid();
|
||||||
|
return (
|
||||||
|
<FormFieldUIDContext.Provider value={name}>
|
||||||
|
<BlockItem>{props.children}</BlockItem>
|
||||||
|
</FormFieldUIDContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Form.__Decorator = ({ children }) => children;
|
||||||
|
Form.DesignableBar = DesignableBar;
|
||||||
|
Form.Field.DesignableBar = FieldDesignableBar;
|
||||||
|
@ -89,6 +89,7 @@ export const Grid: any = observer((props: any) => {
|
|||||||
// const schema = useFieldSchema();
|
// const schema = useFieldSchema();
|
||||||
const gridPath = useSchemaPath();
|
const gridPath = useSchemaPath();
|
||||||
const {
|
const {
|
||||||
|
designable,
|
||||||
schema,
|
schema,
|
||||||
schema: designableSchema,
|
schema: designableSchema,
|
||||||
insertAfter,
|
insertAfter,
|
||||||
@ -166,7 +167,7 @@ export const Grid: any = observer((props: any) => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{AddNewComponent && <AddNewComponent />}
|
{designable && AddNewComponent && <AddNewComponent />}
|
||||||
</div>
|
</div>
|
||||||
</DragDropManagerProvider>
|
</DragDropManagerProvider>
|
||||||
);
|
);
|
||||||
@ -187,7 +188,7 @@ Grid.Row = observer((props) => {
|
|||||||
deepRemove,
|
deepRemove,
|
||||||
} = useDesignable();
|
} = useDesignable();
|
||||||
const len = Object.keys(designableSchema.properties || {}).length;
|
const len = Object.keys(designableSchema.properties || {}).length;
|
||||||
console.log({ len, schema, designableSchema });
|
// console.log({ len, schema, designableSchema });
|
||||||
return (
|
return (
|
||||||
<ColumnSizeContext.Provider value={len}>
|
<ColumnSizeContext.Provider value={len}>
|
||||||
<ColDivider
|
<ColDivider
|
||||||
|
@ -6,7 +6,7 @@ import { createContext } from 'react';
|
|||||||
|
|
||||||
import { extend } from 'umi-request';
|
import { extend } from 'umi-request';
|
||||||
import { isVoidField } from '@formily/core';
|
import { isVoidField } from '@formily/core';
|
||||||
import { useRequest } from 'ahooks';
|
import { useCookieState, useRequest } from 'ahooks';
|
||||||
|
|
||||||
export * from '../components/schema-renderer';
|
export * from '../components/schema-renderer';
|
||||||
|
|
||||||
@ -14,19 +14,19 @@ export function mapReadPretty(component, readPrettyProps) {
|
|||||||
return function (target) {
|
return function (target) {
|
||||||
return observer(
|
return observer(
|
||||||
(props) => {
|
(props) => {
|
||||||
const field = useField()
|
const field = useField();
|
||||||
if (!isVoidField(field) && field?.pattern === 'readPretty') {
|
if (!isVoidField(field) && field?.pattern === 'readPretty') {
|
||||||
return React.createElement(component, {
|
return React.createElement(component, {
|
||||||
...readPrettyProps,
|
...readPrettyProps,
|
||||||
...props,
|
...props,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
return React.createElement(target, props)
|
return React.createElement(target, props);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
forwardRef: true,
|
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) {
|
export async function deleteCollection(name) {
|
||||||
await request('collections:destroy', {
|
await request('collections:destroy', {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@ -109,3 +116,16 @@ const [CollectionContextProvider, useCollectionContext] = constate(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export { CollectionContextProvider, useCollectionContext };
|
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 };
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
import React from 'react'
|
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 { Input as AntdInput } from 'antd'
|
||||||
import { InputProps, TextAreaProps } from 'antd/lib/input'
|
import { InputProps, TextAreaProps } from 'antd/lib/input'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
|
||||||
import { Display } from '../display';
|
import { Display } from '../display';
|
||||||
import { DesignableBar } from './DesignableBar';
|
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<InputProps> & {
|
type ComposedInput = React.FC<InputProps> & {
|
||||||
TextArea?: React.FC<TextAreaProps>
|
TextArea?: React.FC<TextAreaProps>
|
||||||
@ -15,6 +24,7 @@ type ComposedInput = React.FC<InputProps> & {
|
|||||||
export const Input: ComposedInput = connect(
|
export const Input: ComposedInput = connect(
|
||||||
AntdInput,
|
AntdInput,
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
|
console.log('fieldfieldfieldfieldfield', field.address.entire);
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
suffix: (
|
suffix: (
|
||||||
@ -31,8 +41,65 @@ export const Input: ComposedInput = connect(
|
|||||||
mapReadPretty(Display.Input)
|
mapReadPretty(Display.Input)
|
||||||
)
|
)
|
||||||
|
|
||||||
Input.DesignableBar = DesignableBar;
|
|
||||||
Input.TextArea = connect(AntdInput.TextArea, mapReadPretty(Display.TextArea))
|
Input.TextArea = connect(AntdInput.TextArea, mapReadPretty(Display.TextArea))
|
||||||
Input.URL = connect(AntdInput, mapReadPretty(Display.URL))
|
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 (
|
||||||
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Space size={'small'}>
|
||||||
|
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||||
|
{dragRef && <DragOutlined ref={dragRef} />}
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item
|
||||||
|
key={'update'}
|
||||||
|
onClick={() => {
|
||||||
|
console.log({ root, currentSchema, schema, fieldSchema }, getSchemaPath(fieldSchema));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改 Input
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
key={'delete'}
|
||||||
|
onClick={async () => {
|
||||||
|
const removed = deepRemove();
|
||||||
|
// console.log({ removed })
|
||||||
|
const last = removed.pop();
|
||||||
|
if (isGridRowOrCol(last)) {
|
||||||
|
await removeSchema(last);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除当前区块
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Input
|
export default Input
|
||||||
|
@ -20,6 +20,7 @@ import { DraggableBlockContext } from '../../components/drag-and-drop';
|
|||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { removeSchema, updateSchema } from '..';
|
import { removeSchema, updateSchema } from '..';
|
||||||
import { isGridRowOrCol } from '../grid';
|
import { isGridRowOrCol } from '../grid';
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
export const Markdown: any = connect(
|
export const Markdown: any = connect(
|
||||||
(props) => {
|
(props) => {
|
||||||
@ -67,7 +68,7 @@ export const Markdown: any = connect(
|
|||||||
console.log('Markdown', props);
|
console.log('Markdown', props);
|
||||||
let text = props.value;
|
let text = props.value;
|
||||||
let value = (
|
let value = (
|
||||||
<div dangerouslySetInnerHTML={{ __html: micromark(text || '') }} />
|
<div className={'nb-markdown'} dangerouslySetInnerHTML={{ __html: micromark(text || '') }} />
|
||||||
);
|
);
|
||||||
return <Display.TextArea {...props} text={text} value={value} />;
|
return <Display.TextArea {...props} text={text} value={value} />;
|
||||||
}),
|
}),
|
||||||
@ -75,9 +76,12 @@ export const Markdown: any = connect(
|
|||||||
|
|
||||||
Markdown.DesignableBar = observer((props) => {
|
Markdown.DesignableBar = observer((props) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { schema, refresh, deepRemove } = useDesignable();
|
const { designable, schema, refresh, deepRemove } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { dragRef } = useContext(DraggableBlockContext);
|
const { dragRef } = useContext(DraggableBlockContext);
|
||||||
|
if (!designable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -117,7 +121,67 @@ Markdown.DesignableBar = observer((props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除当前区块
|
删除当前文本
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Space size={'small'}>
|
||||||
|
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
|
||||||
|
{dragRef && <DragOutlined ref={dragRef} />}
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item
|
||||||
|
key={'update'}
|
||||||
|
onClick={() => {
|
||||||
|
field.readPretty = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改文本内容
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
key={'delete'}
|
||||||
|
onClick={async () => {
|
||||||
|
const removed = deepRemove();
|
||||||
|
// console.log({ removed })
|
||||||
|
const last = removed.pop();
|
||||||
|
if (isGridRowOrCol(last)) {
|
||||||
|
await removeSchema(last);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除当前文本
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
|
3
packages/client/src/schemas/markdown/style.less
Normal file
3
packages/client/src/schemas/markdown/style.less
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.nb-markdown > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
@ -97,7 +97,7 @@ const SideMenu = (props: any) => {
|
|||||||
|
|
||||||
export const Menu: any = observer((props: any) => {
|
export const Menu: any = observer((props: any) => {
|
||||||
const { mode, onSelect, sideMenuRef, ...others } = props;
|
const { mode, onSelect, sideMenuRef, ...others } = props;
|
||||||
const { schema } = useDesignable();
|
const { designable, schema } = useDesignable();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
console.log('Menu.schema', schema, fieldSchema);
|
console.log('Menu.schema', schema, fieldSchema);
|
||||||
const [selectedKey, setSelectedKey] = useState(null);
|
const [selectedKey, setSelectedKey] = useState(null);
|
||||||
@ -275,8 +275,10 @@ Menu.SubMenu = observer((props: any) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Menu.AddNew = 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 = {
|
const schemas = {
|
||||||
'Menu.Link': {
|
'Menu.Link': {
|
||||||
icon: <MenuOutlined />,
|
icon: <MenuOutlined />,
|
||||||
@ -516,11 +518,15 @@ Menu.DesignableBar = (props) => {
|
|||||||
|
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { schema, remove, refresh, insertAfter, insertBefore, appendChild } =
|
const { designable, schema, remove, refresh, insertAfter, insertBefore, appendChild } =
|
||||||
useDesignable();
|
useDesignable();
|
||||||
const formConfig = schemas[schema['x-component']];
|
const formConfig = schemas[schema['x-component']];
|
||||||
const isSubMenu = schema['x-component'] === 'Menu.SubMenu';
|
const isSubMenu = schema['x-component'] === 'Menu.SubMenu';
|
||||||
|
|
||||||
|
if (!designable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<div
|
<div
|
||||||
|
@ -32,10 +32,11 @@ import {
|
|||||||
SortableElement,
|
SortableElement,
|
||||||
} from 'react-sortable-hoc';
|
} from 'react-sortable-hoc';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { getSchemaPath, useDesignable, useSchemaPath, VisibleContext } from '../';
|
import { getSchemaPath, removeSchema, useDesignable, useSchemaPath, VisibleContext } from '../';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||||
import AddNew from '../add-new';
|
import AddNew from '../add-new';
|
||||||
|
import { isGridRowOrCol } from '../grid';
|
||||||
|
|
||||||
interface TableRowProps {
|
interface TableRowProps {
|
||||||
index: number;
|
index: number;
|
||||||
@ -446,7 +447,6 @@ function useDesignableBar() {
|
|||||||
Table.Column = observer((props) => {
|
Table.Column = observer((props) => {
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
console.log('Table.Column', schema, field.title);
|
|
||||||
const { DesignableBar } = useDesignableBar();
|
const { DesignableBar } = useDesignableBar();
|
||||||
return (
|
return (
|
||||||
<div className={'nb-table-column'}>
|
<div className={'nb-table-column'}>
|
||||||
@ -461,7 +461,6 @@ Table.Column.DesignableBar = () => {
|
|||||||
// const fieldSchema = useFieldSchema();
|
// const fieldSchema = useFieldSchema();
|
||||||
const { schema, remove, refresh, insertAfter } = useDesignable();
|
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
console.log('Table.Column.DesignableBar', { schema });
|
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -571,7 +570,7 @@ Table.Index = observer((props) => {
|
|||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const field = useField<Formily.Core.Models.Field>();
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
return <div>#{index + 1}</div>;
|
return <div>{index + 1}</div>;
|
||||||
});
|
});
|
||||||
|
|
||||||
Table.Addition = observer((props: any) => {
|
Table.Addition = observer((props: any) => {
|
||||||
@ -605,7 +604,7 @@ Table.Action.DesignableBar = () => {
|
|||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
const { schema, remove, refresh, insertAfter } = useDesignable();
|
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
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 (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -638,9 +637,12 @@ Table.Action.DesignableBar = () => {
|
|||||||
|
|
||||||
Table.DesignableBar = observer((props) => {
|
Table.DesignableBar = observer((props) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { schema, refresh, deepRemove } = useDesignable();
|
const { designable, schema, refresh, deepRemove } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { dragRef } = useContext(DraggableBlockContext);
|
const { dragRef } = useContext(DraggableBlockContext);
|
||||||
|
if (!designable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -671,8 +673,13 @@ Table.DesignableBar = observer((props) => {
|
|||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key={'delete'}
|
key={'delete'}
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
deepRemove();
|
const removed = deepRemove();
|
||||||
|
// console.log({ removed })
|
||||||
|
const last = removed.pop();
|
||||||
|
if (isGridRowOrCol(last)) {
|
||||||
|
await removeSchema(last);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除当前区块
|
删除当前区块
|
||||||
|
Loading…
Reference in New Issue
Block a user