updates
This commit is contained in:
parent
97ca17bc10
commit
e8399e167e
@ -17,7 +17,7 @@ import {
|
|||||||
useHistory,
|
useHistory,
|
||||||
Redirect,
|
Redirect,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import { SchemaRenderer } from '../../schemas';
|
import { CollectionContextProvider, PageTitleContextProvider, SchemaRenderer, usePageTitleContext } from '../../schemas';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
|
||||||
@ -30,6 +30,7 @@ function LayoutWithMenu({ schema }) {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const sideMenuRef = useRef();
|
const sideMenuRef = useRef();
|
||||||
const [activeKey, setActiveKey] = useState(match.params.name);
|
const [activeKey, setActiveKey] = useState(match.params.name);
|
||||||
|
const [,setPageTitle] = usePageTitleContext();
|
||||||
const onSelect = (info) => {
|
const onSelect = (info) => {
|
||||||
console.log('LayoutWithMenu', info);
|
console.log('LayoutWithMenu', info);
|
||||||
if (!info.schema) {
|
if (!info.schema) {
|
||||||
@ -39,6 +40,9 @@ function LayoutWithMenu({ schema }) {
|
|||||||
setActiveKey(null);
|
setActiveKey(null);
|
||||||
} else {
|
} else {
|
||||||
setActiveKey(info.schema.key);
|
setActiveKey(info.schema.key);
|
||||||
|
if (info.schema.title) {
|
||||||
|
setPageTitle(info.schema.title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
console.log({ match });
|
console.log({ match });
|
||||||
@ -98,7 +102,13 @@ export function AdminLayout({ route }: any) {
|
|||||||
return <Spin />;
|
return <Spin />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <LayoutWithMenu schema={data} />;
|
return (
|
||||||
|
<CollectionContextProvider>
|
||||||
|
<PageTitleContextProvider>
|
||||||
|
<LayoutWithMenu schema={data} />
|
||||||
|
</PageTitleContextProvider>
|
||||||
|
</CollectionContextProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminLayout;
|
export default AdminLayout;
|
||||||
|
@ -46,8 +46,10 @@ import { TimePicker } from '../../schemas/time-picker';
|
|||||||
import { Upload } from '../../schemas/upload';
|
import { Upload } from '../../schemas/upload';
|
||||||
import { FormItem } from '../../schemas/form-item';
|
import { FormItem } from '../../schemas/form-item';
|
||||||
import { BlockItem } from '../../schemas/block-item';
|
import { BlockItem } from '../../schemas/block-item';
|
||||||
|
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';
|
||||||
|
|
||||||
export const BlockContext = createContext({ dragRef: null });
|
export const BlockContext = createContext({ dragRef: null });
|
||||||
|
|
||||||
@ -59,11 +61,14 @@ interface DesignableContextProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SchemaField = createSchemaField({
|
export const SchemaField = createSchemaField({
|
||||||
scope: {},
|
scope: {
|
||||||
|
Table,
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Card,
|
Card,
|
||||||
Div,
|
Div,
|
||||||
Space,
|
Space,
|
||||||
|
Page,
|
||||||
|
|
||||||
ArrayCollapse,
|
ArrayCollapse,
|
||||||
ArrayTable,
|
ArrayTable,
|
||||||
@ -73,6 +78,7 @@ export const SchemaField = createSchemaField({
|
|||||||
DragAndDrop,
|
DragAndDrop,
|
||||||
|
|
||||||
BlockItem,
|
BlockItem,
|
||||||
|
CardItem,
|
||||||
FormItem,
|
FormItem,
|
||||||
|
|
||||||
Action,
|
Action,
|
||||||
@ -173,6 +179,14 @@ function setKeys(schema: ISchema, parentKey = null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useSchemaComponent(component: string) {
|
||||||
|
if (!component) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const options = useContext(SchemaOptionsContext);
|
||||||
|
return get(options.components, component);
|
||||||
|
}
|
||||||
|
|
||||||
export function useDesignable(path?: any) {
|
export function useDesignable(path?: any) {
|
||||||
const { schema = new Schema({}), refresh } = useContext(DesignableContext);
|
const { schema = new Schema({}), refresh } = useContext(DesignableContext);
|
||||||
const schemaPath = path || useSchemaPath();
|
const schemaPath = path || useSchemaPath();
|
||||||
|
@ -46,9 +46,17 @@ import {
|
|||||||
DownOutlined,
|
DownOutlined,
|
||||||
DatabaseOutlined,
|
DatabaseOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { createSchema, useDesignable, useSchemaPath } from '../';
|
import {
|
||||||
|
createOrUpdateCollection,
|
||||||
|
createSchema,
|
||||||
|
request,
|
||||||
|
useCollectionContext,
|
||||||
|
useDesignable,
|
||||||
|
useSchemaPath,
|
||||||
|
} from '../';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { request } from '../../demos/api';
|
import { useRequest } from 'ahooks';
|
||||||
|
import { SchemaField } from '../../components/schema-renderer';
|
||||||
|
|
||||||
const generateGridBlock = (schema: ISchema) => {
|
const generateGridBlock = (schema: ISchema) => {
|
||||||
const name = schema.name || uid();
|
const name = schema.name || uid();
|
||||||
@ -68,6 +76,10 @@ const generateGridBlock = (schema: ISchema) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isGrid = (schema: Schema) => {
|
||||||
|
return schema['x-component'] === 'Grid';
|
||||||
|
};
|
||||||
|
|
||||||
const isGridBlock = (schema: Schema) => {
|
const isGridBlock = (schema: Schema) => {
|
||||||
if (schema.parent['x-component'] !== 'Grid.Col') {
|
if (schema.parent['x-component'] !== 'Grid.Col') {
|
||||||
return false;
|
return false;
|
||||||
@ -83,14 +95,519 @@ const isGridBlock = (schema: Schema) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function generateCardItemSchema(component) {
|
||||||
|
const defaults = {
|
||||||
|
Markdown: {
|
||||||
|
type: 'string',
|
||||||
|
default: '这是一段演示文字',
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-component': 'Markdown',
|
||||||
|
'x-component-props': {
|
||||||
|
savedInSchema: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: {
|
||||||
|
type: 'array',
|
||||||
|
'x-designable-bar': 'Table.DesignableBar',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Table',
|
||||||
|
default: [
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
{ key: uid(), field1: uid(), field2: uid() },
|
||||||
|
],
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'key',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
[`action_bar_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Table.ActionBar',
|
||||||
|
properties: {
|
||||||
|
action1: {
|
||||||
|
type: 'void',
|
||||||
|
name: 'action1',
|
||||||
|
title: '筛选',
|
||||||
|
'x-component': 'Action',
|
||||||
|
properties: {
|
||||||
|
popover1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '弹窗标题',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-component': 'Action.Popover',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
filter: {
|
||||||
|
name: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'Filter',
|
||||||
|
properties: {
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '字段1',
|
||||||
|
'x-component': 'Filter.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
operations: [
|
||||||
|
{ label: '等于', value: 'eq' },
|
||||||
|
{ label: '不等于', value: 'ne' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column2: {
|
||||||
|
type: 'void',
|
||||||
|
title: '字段2',
|
||||||
|
'x-component': 'Filter.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
operations: [
|
||||||
|
{ label: '大于', value: 'gt' },
|
||||||
|
{ label: '小于', value: 'lt' },
|
||||||
|
{
|
||||||
|
label: '非空',
|
||||||
|
value: 'notNull',
|
||||||
|
noValue: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'number',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action2: {
|
||||||
|
type: 'void',
|
||||||
|
name: 'action1',
|
||||||
|
title: '新增',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-designable-bar': 'Action.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
drawer1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '抽屉标题',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-component': 'Action.Modal',
|
||||||
|
'x-component-props': {
|
||||||
|
useOkAction: '{{ Table.useTableCreateAction }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'string',
|
||||||
|
title: '字段1',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
field2: {
|
||||||
|
type: 'string',
|
||||||
|
title: '字段2',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action3: {
|
||||||
|
type: 'void',
|
||||||
|
name: 'action1',
|
||||||
|
title: '删除',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ Table.useTableDestroyAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`a_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Table.ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
align: 'bottom',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
pagination: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Table.Pagination',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`column_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '排序',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Table.SortHandle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`column_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '序号',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
index: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Table.Index',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`column_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '字段1',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
// title: 'z1',
|
||||||
|
},
|
||||||
|
'x-designable-bar': 'Table.Column.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator-props': {
|
||||||
|
feedbackLayout: 'popover',
|
||||||
|
},
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`column_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '字段2',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
'x-designable-bar': 'Table.Column.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
field2: {
|
||||||
|
type: 'string',
|
||||||
|
// title: '字段2',
|
||||||
|
required: true,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator-props': {
|
||||||
|
feedbackLayout: 'popover',
|
||||||
|
},
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[`column_${uid()}`]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '操作',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
name: 'action1',
|
||||||
|
title: '查看',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
drawer1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '查看',
|
||||||
|
'x-component': 'Action.Modal',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-component-props': {
|
||||||
|
tab: 'Tab1',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
aaa: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'AAA',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
required: true,
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tab2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-component-props': {
|
||||||
|
tab: 'Tab2',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
bbb: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'BBB',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
required: true,
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
name: 'action1',
|
||||||
|
title: '修改',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
drawer1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '编辑表单',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-decorator-props': {
|
||||||
|
useValues: '{{ Table.useTableRow }}',
|
||||||
|
},
|
||||||
|
'x-component': 'Action.Modal',
|
||||||
|
'x-component-props': {
|
||||||
|
useOkAction: '{{ Table.useTableUpdateAction }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'string',
|
||||||
|
title: '字段1',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
field2: {
|
||||||
|
type: 'string',
|
||||||
|
title: '字段2',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '删除',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ Table.useTableDestroyAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Form: {
|
||||||
|
type: 'void',
|
||||||
|
name: uid(),
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Form',
|
||||||
|
'x-designable-bar': 'Form.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-component-props': {
|
||||||
|
addNewComponent: 'AddNew.FormItem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return defaults[component];
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFormItemSchema(component) {
|
||||||
|
const defaults = {
|
||||||
|
Markdown: {
|
||||||
|
type: 'string',
|
||||||
|
title: uid(),
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Markdown',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return defaults[component];
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
title: '数据表名称',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
title: '数据表标识',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
type: 'array',
|
||||||
|
title: '数据表字段',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'DatabaseField',
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const AddNew = () => null;
|
export const AddNew = () => null;
|
||||||
|
|
||||||
|
AddNew.CardItem = observer((props: any) => {
|
||||||
|
const { ghost, defaultAction } = props;
|
||||||
|
const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
|
||||||
|
const path = useSchemaPath();
|
||||||
|
const { data: collections = [], loading, run } = useRequest('collections:findAll', {
|
||||||
|
formatResult: (result) => result?.data,
|
||||||
|
});
|
||||||
|
console.log({ collections });
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
overlayStyle={{
|
||||||
|
minWidth: 200,
|
||||||
|
}}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
if (visible) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
console.log('onVisibleChange', visible);
|
||||||
|
}}
|
||||||
|
placement={'bottomCenter'}
|
||||||
|
overlay={
|
||||||
|
<Menu
|
||||||
|
onClick={async (info) => {
|
||||||
|
let data: ISchema;
|
||||||
|
if (['addNewTable', 'addNewForm'].includes(info.key)) {
|
||||||
|
const values = await FormDialog(`新建数据表`, () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField schema={dbSchema} />
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
name: `t_${uid()}`,
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await createOrUpdateCollection(values);
|
||||||
|
data = generateCardItemSchema(
|
||||||
|
info.key === 'addNewTable' ? 'Table' : 'Form',
|
||||||
|
);
|
||||||
|
} else if (info.key !== 'Markdown') {
|
||||||
|
const keys = info.key.split('.');
|
||||||
|
const component = keys.shift();
|
||||||
|
const tableName = keys.join('.');
|
||||||
|
data = generateCardItemSchema(component);
|
||||||
|
console.log('info.keyPath', component, tableName);
|
||||||
|
} else {
|
||||||
|
data = generateCardItemSchema(info.key);
|
||||||
|
}
|
||||||
|
if (schema['key']) {
|
||||||
|
data['key'] = uid();
|
||||||
|
}
|
||||||
|
if (isGridBlock(schema)) {
|
||||||
|
path.pop();
|
||||||
|
path.pop();
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
} else if (isGrid(schema)) {
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
let s;
|
||||||
|
if (isGrid(schema)) {
|
||||||
|
s = appendChild(data, [...path]);
|
||||||
|
} else if (defaultAction === 'insertAfter') {
|
||||||
|
s = insertAfter(data, [...path]);
|
||||||
|
} else {
|
||||||
|
s = insertBefore(data, [...path]);
|
||||||
|
}
|
||||||
|
await createSchema(s);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Menu.SubMenu key={'Table'} title={'新建表格'}>
|
||||||
|
<Menu.ItemGroup key={'table-select'} title={'选择数据表'}>
|
||||||
|
{collections.map((item) => (
|
||||||
|
<Menu.Item key={`Table.${item.name}`}>{item.title}</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
<Menu.Divider></Menu.Divider>
|
||||||
|
<Menu.Item key={'addNewTable'}>新建数据表</Menu.Item>
|
||||||
|
</Menu.SubMenu>
|
||||||
|
<Menu.SubMenu key={'Form'} title={'新建表单'}>
|
||||||
|
<Menu.ItemGroup key={'form-select'} title={'选择数据表'}>
|
||||||
|
{collections.map((item) => (
|
||||||
|
<Menu.Item key={`Form.${item.name}`}>{item.title}</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
<Menu.Divider></Menu.Divider>
|
||||||
|
<Menu.Item key={'addNewForm'}>新建数据表</Menu.Item>
|
||||||
|
</Menu.SubMenu>
|
||||||
|
<Menu.Item key={'Markdown'}>新建 Markdown</Menu.Item>
|
||||||
|
<Menu.SubMenu key={'Ref'} title={'引用模板'}>
|
||||||
|
<Menu.ItemGroup key={'form-select'} title={'选择模板'}>
|
||||||
|
<Menu.Item key={'Ref.name1'}>模板1</Menu.Item>
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
<Menu.Divider></Menu.Divider>
|
||||||
|
<Menu.Item key={'addNewRef'}>新建模板</Menu.Item>
|
||||||
|
</Menu.SubMenu>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ghost ? (
|
||||||
|
<PlusOutlined />
|
||||||
|
) : (
|
||||||
|
<Button block type={'dashed'} icon={<PlusOutlined />}></Button>
|
||||||
|
)}
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
AddNew.BlockItem = observer((props: any) => {
|
AddNew.BlockItem = observer((props: any) => {
|
||||||
const { ghost, defaultAction } = props;
|
const { ghost, defaultAction } = props;
|
||||||
const { schema, insertBefore, insertAfter } = useDesignable();
|
const { schema, insertBefore, insertAfter } = useDesignable();
|
||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
|
overlayStyle={{
|
||||||
|
minWidth: 200,
|
||||||
|
}}
|
||||||
|
placement={'bottomCenter'}
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.SubMenu title={'新建表格'}>
|
<Menu.SubMenu title={'新建表格'}>
|
||||||
@ -113,7 +630,7 @@ AddNew.BlockItem = observer((props: any) => {
|
|||||||
type: 'void',
|
type: 'void',
|
||||||
title: uid(),
|
title: uid(),
|
||||||
'x-designable-bar': 'BlockItem.DesignableBar',
|
'x-designable-bar': 'BlockItem.DesignableBar',
|
||||||
'x-decorator': 'BlockItem',
|
'x-decorator': 'CardItem',
|
||||||
'x-component': 'Markdown',
|
'x-component': 'Markdown',
|
||||||
};
|
};
|
||||||
if (schema['key']) {
|
if (schema['key']) {
|
||||||
@ -141,7 +658,11 @@ AddNew.BlockItem = observer((props: any) => {
|
|||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{ghost ? <PlusOutlined /> : <Button icon={<PlusOutlined />}></Button>}
|
{ghost ? (
|
||||||
|
<PlusOutlined />
|
||||||
|
) : (
|
||||||
|
<Button block type={'dashed'} icon={<PlusOutlined />}></Button>
|
||||||
|
)}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -152,6 +673,10 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
|
overlayStyle={{
|
||||||
|
minWidth: 200,
|
||||||
|
}}
|
||||||
|
placement={'bottomCenter'}
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.ItemGroup title={'选择已有字段'}>
|
<Menu.ItemGroup title={'选择已有字段'}>
|
||||||
@ -159,25 +684,24 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
style={{
|
style={{
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
let data: ISchema = {
|
let data: ISchema = generateFormItemSchema('Markdown');
|
||||||
type: 'void',
|
if (schema['key']) {
|
||||||
title: uid(),
|
data['key'] = uid();
|
||||||
'x-designable-bar': 'FormItem.DesignableBar',
|
}
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Markdown',
|
|
||||||
};
|
|
||||||
if (isGridBlock(schema)) {
|
if (isGridBlock(schema)) {
|
||||||
path.pop();
|
path.pop();
|
||||||
path.pop();
|
path.pop();
|
||||||
data = generateGridBlock(data);
|
data = generateGridBlock(data);
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
|
let s;
|
||||||
if (defaultAction === 'insertAfter') {
|
if (defaultAction === 'insertAfter') {
|
||||||
insertAfter(data, [...path]);
|
s = insertAfter(data, [...path]);
|
||||||
} else {
|
} else {
|
||||||
insertBefore(data, [...path]);
|
s = insertBefore(data, [...path]);
|
||||||
}
|
}
|
||||||
|
await createSchema(s);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -219,7 +743,11 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{ghost ? <PlusOutlined /> : <Button icon={<PlusOutlined />}></Button>}
|
{ghost ? (
|
||||||
|
<PlusOutlined />
|
||||||
|
) : (
|
||||||
|
<Button block type={'dashed'} icon={<PlusOutlined />}></Button>
|
||||||
|
)}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -113,17 +113,6 @@ export const BlockItem: any = observer((props) => {
|
|||||||
return React.createElement(uid ? DraggableBlock : Block, props);
|
return React.createElement(uid ? DraggableBlock : Block, props);
|
||||||
});
|
});
|
||||||
|
|
||||||
// export const BlockItem: any = connect((props) => {
|
|
||||||
// const { children, ...others } = props;
|
|
||||||
// const { DesignableBar } = useDesignableBar();
|
|
||||||
// return (
|
|
||||||
// <div {...others} style={{marginBottom: 24}} className={'designable-form-item'}>
|
|
||||||
// {children}
|
|
||||||
// <DesignableBar />
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
BlockItem.DesignableBar = () => {
|
BlockItem.DesignableBar = () => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { schema, refresh } = useDesignable();
|
const { schema, refresh } = useDesignable();
|
||||||
|
32
packages/client/src/schemas/card-item/index.tsx
Normal file
32
packages/client/src/schemas/card-item/index.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React, { useContext, useState } from 'react';
|
||||||
|
import {
|
||||||
|
connect,
|
||||||
|
mapProps,
|
||||||
|
mapReadPretty,
|
||||||
|
observer,
|
||||||
|
SchemaOptionsContext,
|
||||||
|
useField,
|
||||||
|
useFieldSchema,
|
||||||
|
} from '@formily/react';
|
||||||
|
import { FormItem as FormilyFormItem } from '@formily/antd';
|
||||||
|
import { Card, Dropdown, Menu, Space } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||||
|
// import './style.less';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { GridBlockContext } from '../grid';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { useDesignable } from '../';
|
||||||
|
import { AddNew } from '../add-new';
|
||||||
|
import { BlockItem } from '../block-item';
|
||||||
|
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||||
|
|
||||||
|
export const CardItem: any = connect((props) => {
|
||||||
|
return (
|
||||||
|
<BlockItem>
|
||||||
|
<Card bordered={false} {...props}>
|
||||||
|
{props.children}
|
||||||
|
</Card>
|
||||||
|
</BlockItem>
|
||||||
|
);
|
||||||
|
});
|
@ -39,7 +39,7 @@ import Modal from 'antd/lib/modal/Modal';
|
|||||||
import { clone, cloneDeep, get } from 'lodash';
|
import { clone, cloneDeep, get } from 'lodash';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { createOrUpdateCollection, deleteCollection } from '..';
|
import { createOrUpdateCollection, deleteCollection, useCollectionContext } from '..';
|
||||||
|
|
||||||
export const DatabaseCollection = observer((props) => {
|
export const DatabaseCollection = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.ArrayField>();
|
const field = useField<Formily.Core.Models.ArrayField>();
|
||||||
@ -49,6 +49,7 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const [newValue, setNewValue] = useState('');
|
const [newValue, setNewValue] = useState('');
|
||||||
|
const { refresh } = useCollectionContext();
|
||||||
|
|
||||||
const { run, loading } = useRequest('collections:findAll', {
|
const { run, loading } = useRequest('collections:findAll', {
|
||||||
formatResult: (result) => result?.data,
|
formatResult: (result) => result?.data,
|
||||||
@ -125,6 +126,7 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
setNewValue('');
|
setNewValue('');
|
||||||
await createOrUpdateCollection(data);
|
await createOrUpdateCollection(data);
|
||||||
|
await refresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -166,6 +168,7 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
}
|
}
|
||||||
if (item.name) {
|
if (item.name) {
|
||||||
await deleteCollection(item.name);
|
await deleteCollection(item.name);
|
||||||
|
await refresh();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -188,6 +191,7 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
await form.validate(`${field.address.entire}.${activeIndex}.*`);
|
await form.validate(`${field.address.entire}.${activeIndex}.*`);
|
||||||
delete field.value[activeIndex]['unsaved'];
|
delete field.value[activeIndex]['unsaved'];
|
||||||
await createOrUpdateCollection(field.value[activeIndex]);
|
await createOrUpdateCollection(field.value[activeIndex]);
|
||||||
|
await refresh();
|
||||||
message.success('保存成功');
|
message.success('保存成功');
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}}
|
}}
|
||||||
|
@ -8,14 +8,18 @@ import {
|
|||||||
SchemaExpressionScopeContext,
|
SchemaExpressionScopeContext,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
ISchema,
|
ISchema,
|
||||||
|
useField,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { useSchemaPath, SchemaField, useDesignable } from '../';
|
import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { Dropdown, Menu } from 'antd';
|
import { Dropdown, Menu, Space } from 'antd';
|
||||||
import { MenuOutlined } 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';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
import AddNew from '../add-new';
|
||||||
|
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||||
|
import { isGridRowOrCol } from '../grid';
|
||||||
|
|
||||||
export const Form: any = observer((props: any) => {
|
export const Form: any = observer((props: any) => {
|
||||||
const { useValues = () => ({}), ...others } = props;
|
const { useValues = () => ({}), ...others } = props;
|
||||||
@ -61,21 +65,59 @@ export const Form: any = observer((props: any) => {
|
|||||||
|
|
||||||
Form.__Decorator = ({ children }) => children;
|
Form.__Decorator = ({ children }) => children;
|
||||||
|
|
||||||
Form.DesignableBar = (props) => {
|
Form.DesignableBar = observer((props) => {
|
||||||
const { active } = props;
|
const field = useField();
|
||||||
|
const { schema, refresh, deepRemove } = useDesignable();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { dragRef } = useContext(DraggableBlockContext);
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<div className={'designable-bar-actions'}>
|
<span
|
||||||
<Dropdown
|
onClick={(e) => {
|
||||||
overlay={
|
e.stopPropagation();
|
||||||
<Menu>
|
}}
|
||||||
<Menu.Item>表单配置</Menu.Item>
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
</Menu>
|
>
|
||||||
}
|
<Space size={'small'}>
|
||||||
>
|
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||||
<MenuOutlined />
|
{dragRef && <DragOutlined ref={dragRef} />}
|
||||||
</Dropdown>
|
<Dropdown
|
||||||
</div>
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -14,6 +14,9 @@ const schema: ISchema = {
|
|||||||
type: 'void',
|
type: 'void',
|
||||||
name: uid(),
|
name: uid(),
|
||||||
'x-component': 'Grid',
|
'x-component': 'Grid',
|
||||||
|
"x-component-props": {
|
||||||
|
addNewComponent: 'AddNew.CardItem',
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
[`row_${uid()}`]: {
|
[`row_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
useDragDropUID,
|
useDragDropUID,
|
||||||
DragDropManagerProvider,
|
DragDropManagerProvider,
|
||||||
} from '../../components/drag-and-drop';
|
} from '../../components/drag-and-drop';
|
||||||
|
import { useSchemaComponent } from '../../components/schema-renderer';
|
||||||
|
|
||||||
const ColumnSizeContext = createContext(null);
|
const ColumnSizeContext = createContext(null);
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ export const GridBlockContext = createContext({
|
|||||||
dragRef: null,
|
dragRef: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
function isGridRowOrCol(schema: ISchema) {
|
export function isGridRowOrCol(schema: ISchema) {
|
||||||
return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']);
|
return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,11 +82,14 @@ const ColDivider = (props: any) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Grid: any = observer((props) => {
|
export const Grid: any = observer((props: any) => {
|
||||||
|
const { addNewComponent } = props;
|
||||||
|
const AddNewComponent = useSchemaComponent(addNewComponent);
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
const schema = useFieldSchema();
|
// const schema = useFieldSchema();
|
||||||
const gridPath = useSchemaPath();
|
const gridPath = useSchemaPath();
|
||||||
const {
|
const {
|
||||||
|
schema,
|
||||||
schema: designableSchema,
|
schema: designableSchema,
|
||||||
insertAfter,
|
insertAfter,
|
||||||
prepend,
|
prepend,
|
||||||
@ -162,6 +166,7 @@ export const Grid: any = observer((props) => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{AddNewComponent && <AddNewComponent />}
|
||||||
</div>
|
</div>
|
||||||
</DragDropManagerProvider>
|
</DragDropManagerProvider>
|
||||||
);
|
);
|
||||||
@ -169,9 +174,10 @@ export const Grid: any = observer((props) => {
|
|||||||
|
|
||||||
Grid.Row = observer((props) => {
|
Grid.Row = observer((props) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const schema = useFieldSchema();
|
// const schema = useFieldSchema();
|
||||||
const rowPath = useSchemaPath();
|
const rowPath = useSchemaPath();
|
||||||
const {
|
const {
|
||||||
|
schema,
|
||||||
schema: designableSchema,
|
schema: designableSchema,
|
||||||
refresh,
|
refresh,
|
||||||
insertAfter,
|
insertAfter,
|
||||||
|
@ -1,18 +1,50 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import React from 'react';
|
||||||
|
import { ISchema, observer, Schema, useField } from '@formily/react';
|
||||||
|
import constate from 'constate';
|
||||||
|
import { useState } from 'react';
|
||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
import { extend } from 'umi-request';
|
||||||
|
import { isVoidField } from '@formily/core';
|
||||||
|
import { useRequest } from 'ahooks';
|
||||||
|
|
||||||
export * from '../components/schema-renderer';
|
export * from '../components/schema-renderer';
|
||||||
|
|
||||||
|
export function mapReadPretty(component, readPrettyProps) {
|
||||||
|
return function (target) {
|
||||||
|
return observer(
|
||||||
|
(props) => {
|
||||||
|
const field = useField()
|
||||||
|
if (!isVoidField(field) && field?.pattern === 'readPretty') {
|
||||||
|
return React.createElement(component, {
|
||||||
|
...readPrettyProps,
|
||||||
|
...props,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return React.createElement(target, props)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
forwardRef: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const VisibleContext = createContext(null);
|
export const VisibleContext = createContext(null);
|
||||||
export const DesignableBarContext = createContext(null);
|
export const DesignableBarContext = createContext(null);
|
||||||
|
|
||||||
|
const [PageTitleContextProvider, usePageTitleContext] = constate(() => {
|
||||||
|
return useState(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { PageTitleContextProvider, usePageTitleContext };
|
||||||
|
|
||||||
export function useDefaultAction() {
|
export function useDefaultAction() {
|
||||||
return {
|
return {
|
||||||
async run () {}
|
async run() {},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
import { extend } from 'umi-request';
|
|
||||||
|
|
||||||
export const request = extend({
|
export const request = extend({
|
||||||
prefix: 'http://localhost:23003/api/',
|
prefix: 'http://localhost:23003/api/',
|
||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
@ -31,7 +63,7 @@ export async function deleteCollection(name) {
|
|||||||
params: {
|
params: {
|
||||||
filter: {
|
filter: {
|
||||||
name,
|
name,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -44,7 +76,7 @@ export async function createSchema(schema: ISchema) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: schema.toJSON(),
|
data: schema.toJSON(),
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
export async function updateSchema(schema: ISchema) {
|
export async function updateSchema(schema: ISchema) {
|
||||||
if (!schema['key']) {
|
if (!schema['key']) {
|
||||||
@ -52,9 +84,9 @@ export async function updateSchema(schema: ISchema) {
|
|||||||
}
|
}
|
||||||
return await request(`ui_schemas:update/${schema.key}`, {
|
return await request(`ui_schemas:update/${schema.key}`, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: schema.toJSON(),
|
data: Schema.isSchemaInstance(schema) ? schema.toJSON() : schema,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
export async function removeSchema(schema: ISchema) {
|
export async function removeSchema(schema: ISchema) {
|
||||||
if (!schema['key']) {
|
if (!schema['key']) {
|
||||||
@ -65,7 +97,15 @@ export async function removeSchema(schema: ISchema) {
|
|||||||
params: {
|
params: {
|
||||||
filter: {
|
filter: {
|
||||||
key: schema['key'],
|
key: schema['key'],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [CollectionContextProvider, useCollectionContext] = constate(() => {
|
||||||
|
return useRequest('collections:findAll', {
|
||||||
|
formatResult: (result) => result?.data,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export { CollectionContextProvider, useCollectionContext };
|
||||||
|
@ -33,7 +33,6 @@ const schema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
input: {
|
input: {
|
||||||
interface: 'string',
|
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: `编辑模式`,
|
title: `编辑模式`,
|
||||||
name: 'name1',
|
name: 'name1',
|
||||||
@ -76,3 +75,58 @@ export default () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Markdown.DesignableBar
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { SchemaRenderer } from '../';
|
||||||
|
|
||||||
|
const schema1 = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
markdown1: {
|
||||||
|
type: 'string',
|
||||||
|
title: `简易Markdown`,
|
||||||
|
default: '这是一段示例文案',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Markdown',
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
|
'x-component-props': {
|
||||||
|
savedInSchema: true,
|
||||||
|
// ellipsis: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema2 = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
markdown1: {
|
||||||
|
type: 'string',
|
||||||
|
title: `简易Markdown`,
|
||||||
|
default: '这是一段示例文案',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Markdown',
|
||||||
|
'x-designable-bar': 'Markdown.DesignableBar',
|
||||||
|
'x-component-props': {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>区块项</h3>
|
||||||
|
<SchemaRenderer schema={schema1} />
|
||||||
|
<h3>表单项</h3>
|
||||||
|
<SchemaRenderer schema={schema2} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
@ -1,15 +1,53 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react';
|
import {
|
||||||
import { Input as AntdInput } from 'antd';
|
connect,
|
||||||
|
mapProps,
|
||||||
|
mapReadPretty,
|
||||||
|
observer,
|
||||||
|
useField,
|
||||||
|
useFieldSchema,
|
||||||
|
} from '@formily/react';
|
||||||
|
import { Button, Dropdown, Input as AntdInput, Menu, Space } from 'antd';
|
||||||
import { InputProps, TextAreaProps } from 'antd/lib/input';
|
import { InputProps, TextAreaProps } from 'antd/lib/input';
|
||||||
import { Display } from '../display';
|
import { Display } from '../display';
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined, MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||||
import micromark from 'micromark';
|
import micromark from 'micromark';
|
||||||
|
import { useDesignable } from '../../components/schema-renderer';
|
||||||
|
import { useContext, useState } from 'react';
|
||||||
|
import AddNew from '../add-new';
|
||||||
|
import cls from 'classnames';
|
||||||
|
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { removeSchema, updateSchema } from '..';
|
||||||
|
import { isGridRowOrCol } from '../grid';
|
||||||
|
|
||||||
export const Markdown = connect(
|
export const Markdown: any = connect(
|
||||||
(props) => {
|
(props) => {
|
||||||
const schema = useFieldSchema();
|
const { schema } = useDesignable();
|
||||||
return <AntdInput.TextArea defaultValue={schema.name} {...props}/>
|
const field = useField<any>();
|
||||||
|
const { savedInSchema, ...others } = props;
|
||||||
|
console.log('Markdown', props);
|
||||||
|
return savedInSchema ? (
|
||||||
|
<div className={'mb-markdown'} style={{ position: 'relative' }}>
|
||||||
|
<AntdInput.TextArea autoSize={{ minRows: 3 }} {...others} />
|
||||||
|
<Button
|
||||||
|
style={{ position: 'absolute', bottom: 5, right: 5 }}
|
||||||
|
type={'primary'}
|
||||||
|
onClick={async () => {
|
||||||
|
field.readPretty = true;
|
||||||
|
schema['default'] = field.value;
|
||||||
|
await updateSchema({
|
||||||
|
key: schema['key'],
|
||||||
|
default: field.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<AntdInput.TextArea autoSize={{ minRows: 1 }} {...others} />
|
||||||
|
);
|
||||||
},
|
},
|
||||||
mapProps((props: any, field) => {
|
mapProps((props: any, field) => {
|
||||||
return {
|
return {
|
||||||
@ -26,6 +64,7 @@ export const Markdown = connect(
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
mapReadPretty((props) => {
|
mapReadPretty((props) => {
|
||||||
|
console.log('Markdown', props);
|
||||||
let text = props.value;
|
let text = props.value;
|
||||||
let value = (
|
let value = (
|
||||||
<div dangerouslySetInnerHTML={{ __html: micromark(text || '') }} />
|
<div dangerouslySetInnerHTML={{ __html: micromark(text || '') }} />
|
||||||
@ -34,4 +73,61 @@ export const Markdown = connect(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Markdown.DesignableBar = observer((props) => {
|
||||||
|
const field = useField();
|
||||||
|
const { schema, refresh, deepRemove } = useDesignable();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { dragRef } = useContext(DraggableBlockContext);
|
||||||
|
return (
|
||||||
|
<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 = 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>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Markdown;
|
export default Markdown;
|
||||||
|
@ -1,71 +1,59 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
export const defaultSchemas = {
|
export function generateDefaultSchema(component) {
|
||||||
MixMenu: {
|
const defaultSchemas: { [key: string]: ISchema } = {
|
||||||
type: 'void',
|
MixMenu: {
|
||||||
'x-component': 'Menu',
|
type: 'void',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-component': 'Menu',
|
||||||
'x-component-props': {
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
sideMenuRef: '{{ sideMenuRef }}',
|
'x-component-props': {
|
||||||
mode: 'mix',
|
sideMenuRef: '{{ sideMenuRef }}',
|
||||||
theme: 'dark',
|
mode: 'mix',
|
||||||
// onSelect: '{{ onSelect }}',
|
theme: 'dark',
|
||||||
|
// onSelect: '{{ onSelect }}',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
'Menu.Item': {
|
||||||
'Menu.Item': {
|
type: 'void',
|
||||||
type: 'void',
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-component': 'Menu.Item',
|
||||||
'x-component': 'Menu.Item',
|
},
|
||||||
},
|
'Menu.Link': {
|
||||||
'Menu.Link': {
|
type: 'void',
|
||||||
type: 'void',
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-component': 'Menu.Link',
|
||||||
'x-component': 'Menu.Link',
|
properties: {
|
||||||
properties: {
|
[uid()]: {
|
||||||
[uid()]: {
|
type: 'void',
|
||||||
type: 'void',
|
'x-component': 'Page',
|
||||||
name: uid(),
|
properties: {
|
||||||
'x-component': 'Grid',
|
[uid()]: {
|
||||||
properties: {
|
type: 'void',
|
||||||
[`row_${uid()}`]: {
|
'x-component': 'Grid',
|
||||||
type: 'void',
|
'x-component-props': {
|
||||||
'x-component': 'Grid.Row',
|
addNewComponent: 'AddNew.CardItem',
|
||||||
'x-component-props': {
|
|
||||||
locked: true,
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[`col_${uid()}`]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid.Col',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'AddNew.BlockItem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
'Menu.URL': {
|
||||||
'Menu.URL': {
|
type: 'void',
|
||||||
type: 'void',
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-component': 'Menu.URL',
|
||||||
'x-component': 'Menu.URL',
|
},
|
||||||
},
|
'Menu.SubMenu': {
|
||||||
'Menu.SubMenu': {
|
type: 'void',
|
||||||
type: 'void',
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-component': 'Menu.SubMenu',
|
||||||
'x-component': 'Menu.SubMenu',
|
},
|
||||||
},
|
'Menu.Action': {
|
||||||
'Menu.Action': {
|
type: 'void',
|
||||||
type: 'void',
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-component': 'Menu.Action',
|
||||||
'x-component': 'Menu.Action',
|
},
|
||||||
},
|
};
|
||||||
};
|
return defaultSchemas[component];
|
||||||
|
}
|
||||||
export default defaultSchemas;
|
|
||||||
|
@ -62,7 +62,7 @@ import './style.less';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { findPropertyByPath, getSchemaPath, useSchemaPath } from '../../';
|
import { findPropertyByPath, getSchemaPath, useSchemaPath } from '../../';
|
||||||
import { request } from '../';
|
import { request } from '../';
|
||||||
import defaultSchemas from './defaultSchemas';
|
import { generateDefaultSchema } from './defaultSchemas';
|
||||||
import _, { cloneDeep, get, isNull } from 'lodash';
|
import _, { cloneDeep, get, isNull } from 'lodash';
|
||||||
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
|
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
|
||||||
import deepmerge from 'deepmerge';
|
import deepmerge from 'deepmerge';
|
||||||
@ -369,7 +369,7 @@ Menu.AddNew = observer((props: any) => {
|
|||||||
</FormLayout>
|
</FormLayout>
|
||||||
);
|
);
|
||||||
}).open();
|
}).open();
|
||||||
const defaults = cloneDeep(defaultSchemas[info.key]);
|
const defaults = generateDefaultSchema(info.key);
|
||||||
const data = appendChild(deepmerge(defaults, values));
|
const data = appendChild(deepmerge(defaults, values));
|
||||||
await createSchema(data);
|
await createSchema(data);
|
||||||
}}
|
}}
|
||||||
@ -568,7 +568,7 @@ Menu.DesignableBar = (props) => {
|
|||||||
insertBefore,
|
insertBefore,
|
||||||
appendChild,
|
appendChild,
|
||||||
};
|
};
|
||||||
const defaults = cloneDeep(defaultSchemas[type]);
|
const defaults = generateDefaultSchema(type);
|
||||||
const data = methods[method](deepmerge(defaults, values));
|
const data = methods[method](deepmerge(defaults, values));
|
||||||
await createSchema(data);
|
await createSchema(data);
|
||||||
}}
|
}}
|
||||||
@ -711,11 +711,12 @@ Menu.DesignableBar = (props) => {
|
|||||||
insertBefore,
|
insertBefore,
|
||||||
appendChild,
|
appendChild,
|
||||||
};
|
};
|
||||||
|
const data = schema.toJSON();
|
||||||
|
remove();
|
||||||
const source = methods[values.method](
|
const source = methods[values.method](
|
||||||
schema.toJSON(),
|
data,
|
||||||
values.path,
|
values.path,
|
||||||
);
|
);
|
||||||
remove();
|
|
||||||
await updateSchema(source);
|
await updateSchema(source);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
18
packages/client/src/schemas/page/index.tsx
Normal file
18
packages/client/src/schemas/page/index.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { PageHeader as AntdPageHeader } from 'antd';
|
||||||
|
import { observer } from '@formily/react';
|
||||||
|
import { usePageTitleContext } from '..';
|
||||||
|
|
||||||
|
export const Page = observer((props) => {
|
||||||
|
const { children, ...others } = props;
|
||||||
|
const [pageTitle] = usePageTitleContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AntdPageHeader ghost={false} title={pageTitle} {...others} />
|
||||||
|
<div style={{margin: 24}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -25,7 +25,7 @@ import constate from 'constate';
|
|||||||
import useRequest from '@ahooksjs/use-request';
|
import useRequest from '@ahooksjs/use-request';
|
||||||
import { BaseResult } from '@ahooksjs/use-request/lib/types';
|
import { BaseResult } from '@ahooksjs/use-request/lib/types';
|
||||||
import { uid, clone } from '@formily/shared';
|
import { uid, clone } from '@formily/shared';
|
||||||
import { MenuOutlined } from '@ant-design/icons';
|
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
SortableHandle,
|
SortableHandle,
|
||||||
SortableContainer,
|
SortableContainer,
|
||||||
@ -34,6 +34,8 @@ import {
|
|||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { getSchemaPath, useDesignable, useSchemaPath, VisibleContext } from '../';
|
import { getSchemaPath, useDesignable, useSchemaPath, VisibleContext } from '../';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||||
|
import AddNew from '../add-new';
|
||||||
|
|
||||||
interface TableRowProps {
|
interface TableRowProps {
|
||||||
index: number;
|
index: number;
|
||||||
@ -632,4 +634,56 @@ Table.Action.DesignableBar = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Table.DesignableBar = observer((props) => {
|
||||||
|
const field = useField();
|
||||||
|
const { 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={() => {
|
||||||
|
field.readPretty = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑Markdown
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
key={'delete'}
|
||||||
|
onClick={() => {
|
||||||
|
deepRemove();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除当前区块
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Model } from '@nocobase/database';
|
import { Model } from '@nocobase/database';
|
||||||
|
import deepmerge from 'deepmerge';
|
||||||
|
|
||||||
export class UISchema extends Model {
|
export class UISchema extends Model {
|
||||||
static async create(value?: any, options?: any): Promise<any> {
|
static async create(value?: any, options?: any): Promise<any> {
|
||||||
@ -33,7 +34,7 @@ export class UISchema extends Model {
|
|||||||
];
|
];
|
||||||
const attrs = _.pick(data, keys);
|
const attrs = _.pick(data, keys);
|
||||||
const options = _.omit(data, keys);
|
const options = _.omit(data, keys);
|
||||||
return { ...attrs, options: {...options, ...opts} };
|
return { ...attrs, options: deepmerge(opts, options) };
|
||||||
}
|
}
|
||||||
|
|
||||||
static properties2children(properties = []) {
|
static properties2children(properties = []) {
|
||||||
|
Loading…
Reference in New Issue
Block a user