mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 14:26:36 +00:00
lots of updates
This commit is contained in:
parent
78740f9905
commit
057dc8406a
@ -534,7 +534,7 @@ export async function sort(ctx: Context, next: Next) {
|
||||
const Model = ctx.db.getModel(resourceName);
|
||||
const table = ctx.db.getTable(resourceName);
|
||||
|
||||
const { sticky, field, target } = values;
|
||||
const { sticky, field, target, insertAfter } = values;
|
||||
if (!values.field || typeof target === 'undefined') {
|
||||
return next();
|
||||
}
|
||||
@ -592,11 +592,11 @@ export async function sort(ctx: Context, next: Next) {
|
||||
if (sameScope) {
|
||||
const direction = source[sortAttr] < targetObject[sortAttr] ? {
|
||||
sourceOp: Op.gt,
|
||||
targetOp: Op.lte,
|
||||
targetOp: insertAfter ? Op.lt : Op.lte,
|
||||
increment: -1
|
||||
} : {
|
||||
sourceOp: Op.lt,
|
||||
targetOp: Op.gte,
|
||||
targetOp: insertAfter ? Op.gt : Op.gte,
|
||||
increment: 1
|
||||
};
|
||||
|
||||
@ -617,6 +617,8 @@ export async function sort(ctx: Context, next: Next) {
|
||||
});
|
||||
}
|
||||
|
||||
console.log({ insertAfter, updateWhere })
|
||||
|
||||
await Model.increment(sortAttr, {
|
||||
by: increment,
|
||||
where: updateWhere,
|
||||
@ -624,7 +626,7 @@ export async function sort(ctx: Context, next: Next) {
|
||||
});
|
||||
|
||||
Object.assign(updates, {
|
||||
[sortAttr]: targetObject[sortAttr]
|
||||
[sortAttr]: insertAfter ? targetObject[sortAttr] + 1 : targetObject[sortAttr]
|
||||
});
|
||||
} else {
|
||||
Object.assign(updates, {
|
||||
|
@ -0,0 +1,68 @@
|
||||
import { DatabaseOutlined } from '@ant-design/icons';
|
||||
import { SchemaRenderer } from '../../../';
|
||||
import Modal from 'antd/lib/modal/Modal';
|
||||
import React from 'react';
|
||||
import { useState } from 'react';
|
||||
import schema from './schema';
|
||||
import { Button, Dropdown, Menu } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import { createForm } from '@formily/core';
|
||||
|
||||
export default () => {
|
||||
const form = useMemo(
|
||||
() =>
|
||||
createForm({
|
||||
initialValues: {
|
||||
title: '数据表名称',
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
type={'primary'}
|
||||
>
|
||||
<DatabaseOutlined />
|
||||
</Button>
|
||||
<Modal
|
||||
title={
|
||||
<>
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu
|
||||
onClick={(info) => {
|
||||
form.setValues({
|
||||
title: `数据表${info.key}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Menu.Item key={1}>数据表1</Menu.Item>
|
||||
<Menu.Item key={2}>数据表2</Menu.Item>
|
||||
<Menu.Divider></Menu.Divider>
|
||||
<Menu.Item key={5}>新建数据表</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<span style={{cursor: 'pointer'}}>数据表3</span>
|
||||
</Dropdown>
|
||||
</>
|
||||
}
|
||||
visible={visible}
|
||||
onOk={async () => {
|
||||
await form.submit();
|
||||
setVisible(false);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<SchemaRenderer form={form} schema={schema} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,49 @@
|
||||
import { ISchema } from "@formily/react";
|
||||
|
||||
export default {
|
||||
type: 'object',
|
||||
properties: {
|
||||
form: {
|
||||
type: 'void',
|
||||
"x-component": 'FormLayout',
|
||||
"x-component-props": {
|
||||
layout: 'vertical',
|
||||
},
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
title: '数据表名称',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
title: '字段',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'DatabaseField',
|
||||
default: [
|
||||
{
|
||||
id: 1,
|
||||
interface: 'string',
|
||||
dataType: 'string',
|
||||
name: 'title',
|
||||
ui: {
|
||||
title: '标题',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
dataType: 'text',
|
||||
interface: 'textarea',
|
||||
name: 'content',
|
||||
ui: {
|
||||
title: '内容',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
} as ISchema;
|
@ -23,7 +23,7 @@ import './style.less';
|
||||
|
||||
import { uid } from '@formily/shared';
|
||||
import { ISchema } from '@formily/react';
|
||||
import datatable from './datatable';
|
||||
import DataTableConfig from './DataTableConfig';
|
||||
|
||||
function LayoutWithMenu({ schema }) {
|
||||
const match = useRouteMatch<any>();
|
||||
@ -31,8 +31,15 @@ function LayoutWithMenu({ schema }) {
|
||||
const sideMenuRef = useRef();
|
||||
const [activeKey, setActiveKey] = useState(match.params.name);
|
||||
const onSelect = (info) => {
|
||||
console.log('LayoutWithMenu', schema);
|
||||
setActiveKey(info.key);
|
||||
console.log('LayoutWithMenu', info);
|
||||
if (!info.schema) {
|
||||
setActiveKey(null);
|
||||
} else if (info.schema['x-component'] === 'Menu.SubMenu') {
|
||||
// 实际应该取第一个子元素
|
||||
setActiveKey(null);
|
||||
} else {
|
||||
setActiveKey(info.schema.key);
|
||||
}
|
||||
};
|
||||
console.log({ match });
|
||||
return (
|
||||
@ -46,7 +53,7 @@ function LayoutWithMenu({ schema }) {
|
||||
selectedKeys: [activeKey].filter(Boolean),
|
||||
}}
|
||||
/>
|
||||
<SchemaRenderer schema={datatable} />
|
||||
<DataTableConfig />
|
||||
</Layout.Header>
|
||||
<Layout>
|
||||
<Layout.Sider
|
||||
@ -75,31 +82,7 @@ function Content({ activeKey }) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: uid(),
|
||||
'x-component': 'Grid',
|
||||
properties: {
|
||||
[`row_${uid()}`]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[`col_${uid()}`]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'AddNew.BlockItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return <SchemaRenderer schema={schema} />;
|
||||
return <SchemaRenderer schema={data} />;
|
||||
}
|
||||
|
||||
export function AdminLayout({ route }: any) {
|
||||
|
@ -154,14 +154,24 @@ export function addPropertyAfter(target: Schema, data: ISchema) {
|
||||
});
|
||||
}
|
||||
|
||||
function setKeys(schema: ISchema, parentKey = null) {
|
||||
if (!schema['key']) {
|
||||
schema['key'] = uid();
|
||||
}
|
||||
if (parentKey && !schema['parentKey']) {
|
||||
schema['parentKey'] = parentKey;
|
||||
}
|
||||
Object.keys(schema.properties || {}).forEach((name) => {
|
||||
setKeys(schema.properties[name], schema['key']);
|
||||
});
|
||||
}
|
||||
|
||||
export function useDesignable(path?: any) {
|
||||
const { schema, refresh } = useContext(DesignableContext);
|
||||
const schemaPath = path || useSchemaPath();
|
||||
console.log({ schemaPath });
|
||||
const fieldSchema = useFieldSchema();
|
||||
const currentSchema =
|
||||
findPropertyByPath(schema, schemaPath) || ({} as Schema);
|
||||
console.log({ currentSchema });
|
||||
// console.log('useDesignable', { schema, schemaPath, currentSchema });
|
||||
const options = useContext(SchemaOptionsContext);
|
||||
const DesignableBar =
|
||||
@ -184,9 +194,11 @@ export function useDesignable(path?: any) {
|
||||
}
|
||||
if (target['key']) {
|
||||
property['parentKey'] = target['key'];
|
||||
if (!property['key']) {
|
||||
property['key'] = uid();
|
||||
}
|
||||
setKeys(property);
|
||||
property['__prepend__'] = true;
|
||||
// if (!property['key']) {
|
||||
// property['key'] = uid();
|
||||
// }
|
||||
}
|
||||
const properties = {};
|
||||
properties[property.name] = property;
|
||||
@ -214,9 +226,10 @@ export function useDesignable(path?: any) {
|
||||
}
|
||||
if (target['key']) {
|
||||
property['parentKey'] = target['key'];
|
||||
if (!property['key']) {
|
||||
property['key'] = uid();
|
||||
}
|
||||
// if (!property['key']) {
|
||||
// property['key'] = uid();
|
||||
// }
|
||||
setKeys(property);
|
||||
}
|
||||
target.addProperty(property.name, property);
|
||||
// BUG: 空 properties 时,addProperty 无反应。
|
||||
@ -241,9 +254,8 @@ export function useDesignable(path?: any) {
|
||||
console.log('target.parentKey', target);
|
||||
if (target['parentKey']) {
|
||||
property['parentKey'] = target['parentKey'];
|
||||
if (!property['key']) {
|
||||
property['key'] = uid();
|
||||
}
|
||||
setKeys(property);
|
||||
property['__insertAfter__'] = target['key'];
|
||||
}
|
||||
addPropertyAfter(target, property);
|
||||
refresh();
|
||||
@ -263,9 +275,8 @@ export function useDesignable(path?: any) {
|
||||
}
|
||||
if (target['parentKey']) {
|
||||
property['parentKey'] = target['parentKey'];
|
||||
if (!property['key']) {
|
||||
property['key'] = uid();
|
||||
}
|
||||
setKeys(property);
|
||||
property['__insertBefore__'] = target['key'];
|
||||
}
|
||||
addPropertyBefore(target, property);
|
||||
refresh();
|
||||
@ -280,17 +291,20 @@ export function useDesignable(path?: any) {
|
||||
console.error('target schema does not exist.');
|
||||
return;
|
||||
}
|
||||
const removed = [];
|
||||
const remove = (s: Schema) => {
|
||||
if (!s.parent) {
|
||||
return;
|
||||
}
|
||||
s.parent.removeProperty(s.name);
|
||||
removed.push(s);
|
||||
if (Object.keys(s.parent.properties || {}).length === 0) {
|
||||
remove(s.parent);
|
||||
}
|
||||
};
|
||||
remove(target);
|
||||
refresh();
|
||||
return removed;
|
||||
},
|
||||
remove(targetPath?: any) {
|
||||
let target = currentSchema;
|
||||
|
@ -38,7 +38,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Router initialEntries={['/login']}>
|
||||
<Router initialEntries={['/admin']}>
|
||||
<RouteSwitch routes={data} />
|
||||
</Router>
|
||||
</div>
|
||||
|
@ -46,8 +46,9 @@ import {
|
||||
DownOutlined,
|
||||
DatabaseOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useDesignable, useSchemaPath } from '../';
|
||||
import { createSchema, useDesignable, useSchemaPath } from '../';
|
||||
import { uid } from '@formily/shared';
|
||||
import { request } from '../../demos/api';
|
||||
|
||||
const generateGridBlock = (schema: ISchema) => {
|
||||
const name = schema.name || uid();
|
||||
@ -88,7 +89,6 @@ AddNew.BlockItem = observer((props: any) => {
|
||||
const { ghost, defaultAction } = props;
|
||||
const { schema, insertBefore, insertAfter } = useDesignable();
|
||||
const path = useSchemaPath();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
overlay={
|
||||
@ -108,7 +108,7 @@ AddNew.BlockItem = observer((props: any) => {
|
||||
<Menu.Item>新增数据表</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
onClick={async () => {
|
||||
let data: ISchema = {
|
||||
type: 'void',
|
||||
title: uid(),
|
||||
@ -116,6 +116,9 @@ AddNew.BlockItem = observer((props: any) => {
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Markdown',
|
||||
};
|
||||
if (schema['key']) {
|
||||
data['key'] = uid();
|
||||
}
|
||||
console.log('isGridBlock(schema)', isGridBlock(schema));
|
||||
if (isGridBlock(schema)) {
|
||||
path.pop();
|
||||
@ -123,11 +126,13 @@ AddNew.BlockItem = observer((props: any) => {
|
||||
data = generateGridBlock(data);
|
||||
}
|
||||
if (data) {
|
||||
let s;
|
||||
if (defaultAction === 'insertAfter') {
|
||||
insertAfter(data, [...path]);
|
||||
s = insertAfter(data, [...path]);
|
||||
} else {
|
||||
insertBefore(data, [...path]);
|
||||
s = insertBefore(data, [...path]);
|
||||
}
|
||||
await createSchema(s);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
import './style.less';
|
||||
import cls from 'classnames';
|
||||
|
||||
import { useDesignable, useSchemaPath } from '../';
|
||||
import { createSchema, useDesignable, useSchemaPath } from '../';
|
||||
import {
|
||||
useDrag,
|
||||
useDrop,
|
||||
@ -93,10 +93,10 @@ export const Grid: any = observer((props) => {
|
||||
<RowDivider
|
||||
key={`${schema.name}_0`}
|
||||
name={`${schema.name}_0`}
|
||||
onDrop={(e) => {
|
||||
onDrop={async (e) => {
|
||||
const blockSchema = e.dragItem.schema;
|
||||
const path = [...e.dragItem.path];
|
||||
prepend({
|
||||
const data = prepend({
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
@ -109,6 +109,8 @@ export const Grid: any = observer((props) => {
|
||||
},
|
||||
},
|
||||
});
|
||||
await createSchema(data);
|
||||
console.log('prepend', data);
|
||||
deepRemove(path);
|
||||
}}
|
||||
/>
|
||||
@ -121,10 +123,10 @@ export const Grid: any = observer((props) => {
|
||||
<RowDivider
|
||||
key={`${schema.name}_${index + 1}`}
|
||||
name={`${schema.name}_${index + 1}`}
|
||||
onDrop={(e) => {
|
||||
onDrop={async (e) => {
|
||||
const blockSchema = e.dragItem.schema;
|
||||
const path = [...e.dragItem.path];
|
||||
insertAfter(
|
||||
const data = insertAfter(
|
||||
{
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
@ -140,6 +142,8 @@ export const Grid: any = observer((props) => {
|
||||
},
|
||||
[...gridPath, key],
|
||||
);
|
||||
await createSchema(data);
|
||||
console.log('insertAfter', data);
|
||||
deepRemove(path);
|
||||
}}
|
||||
/>
|
||||
@ -170,15 +174,16 @@ Grid.Row = observer((props) => {
|
||||
<ColumnSizeContext.Provider value={len}>
|
||||
<ColDivider
|
||||
name={`${schema.name}_0`}
|
||||
onDrop={(e) => {
|
||||
onDrop={async (e) => {
|
||||
const blockSchema = e.dragItem.schema;
|
||||
prepend({
|
||||
const data = prepend({
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[blockSchema.name]: blockSchema,
|
||||
},
|
||||
});
|
||||
await createSchema(data);
|
||||
const path = [...e.dragItem.path];
|
||||
deepRemove(path);
|
||||
}}
|
||||
@ -190,9 +195,9 @@ Grid.Row = observer((props) => {
|
||||
<ColDivider
|
||||
name={`${schema.name}_${index + 1}`}
|
||||
resizable={index < len - 1}
|
||||
onDrop={(e) => {
|
||||
onDrop={async (e) => {
|
||||
const blockSchema = e.dragItem.schema;
|
||||
insertAfter(
|
||||
const data = insertAfter(
|
||||
{
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
@ -202,6 +207,8 @@ Grid.Row = observer((props) => {
|
||||
},
|
||||
[...rowPath, key],
|
||||
);
|
||||
console.log('ColDivider', data)
|
||||
await createSchema(data);
|
||||
const path = [...e.dragItem.path];
|
||||
deepRemove(path);
|
||||
}}
|
||||
@ -242,45 +249,3 @@ Grid.Col = observer((props) => {
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
// Grid.Block = observer((props) => {
|
||||
// const schema = useFieldSchema();
|
||||
// const uid = useDragDropUID();
|
||||
// const path = useSchemaPath();
|
||||
// const { isDragging, dragRef, previewRef } = useDrag({
|
||||
// type: uid,
|
||||
// onDragStart() {
|
||||
// console.log('onDragStart');
|
||||
// },
|
||||
// onDragEnd(event) {
|
||||
// console.log('onDragEnd', event.data);
|
||||
// },
|
||||
// onDrag(event) {
|
||||
// // console.log('onDrag');
|
||||
// },
|
||||
// item: {
|
||||
// path,
|
||||
// schema: schema.toJSON(),
|
||||
// },
|
||||
// });
|
||||
// const { isOver, onTopHalf, dropRef } = useDrop({
|
||||
// uid: schema.name,
|
||||
// accept: uid,
|
||||
// data: {},
|
||||
// canDrop: !isDragging,
|
||||
// });
|
||||
// return (
|
||||
// <GridBlockContext.Provider value={{ dragRef }}>
|
||||
// <div
|
||||
// ref={mergeRefs([previewRef, dropRef])}
|
||||
// className={cls('nb-grid-block', {
|
||||
// 'top-half': onTopHalf,
|
||||
// hover: isOver,
|
||||
// dragging: isDragging,
|
||||
// })}
|
||||
// >
|
||||
// {props.children}
|
||||
// </div>
|
||||
// </GridBlockContext.Provider>
|
||||
// );
|
||||
// });
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export * from '../components/schema-renderer';
|
||||
@ -16,3 +17,28 @@ export const request = extend({
|
||||
prefix: 'http://localhost:23003/api/',
|
||||
timeout: 1000,
|
||||
});
|
||||
|
||||
|
||||
export async function createSchema(schema: ISchema) {
|
||||
if (!schema['key']) {
|
||||
return;
|
||||
}
|
||||
return await request('ui_schemas:create', {
|
||||
method: 'post',
|
||||
data: schema.toJSON(),
|
||||
});
|
||||
};
|
||||
|
||||
export async function removeSchema(schema: ISchema) {
|
||||
if (!schema['key']) {
|
||||
return;
|
||||
}
|
||||
await request('ui_schemas:destroy', {
|
||||
method: 'post',
|
||||
params: {
|
||||
filter: {
|
||||
key: schema['key'],
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react';
|
||||
import { Input as AntdInput } from 'antd';
|
||||
import { InputProps, TextAreaProps } from 'antd/lib/input';
|
||||
import { Display } from '../display';
|
||||
@ -7,7 +7,10 @@ import { LoadingOutlined } from '@ant-design/icons';
|
||||
import micromark from 'micromark';
|
||||
|
||||
export const Markdown = connect(
|
||||
AntdInput.TextArea,
|
||||
(props) => {
|
||||
const schema = useFieldSchema();
|
||||
return <AntdInput.TextArea defaultValue={schema.name} {...props}/>
|
||||
},
|
||||
mapProps((props: any, field) => {
|
||||
return {
|
||||
...props,
|
||||
|
71
packages/client/src/schemas/menu/defaultSchemas.ts
Normal file
71
packages/client/src/schemas/menu/defaultSchemas.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
|
||||
export const defaultSchemas = {
|
||||
MixMenu: {
|
||||
type: 'void',
|
||||
'x-component': 'Menu',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
'x-component-props': {
|
||||
sideMenuRef: '{{ sideMenuRef }}',
|
||||
mode: 'mix',
|
||||
theme: 'dark',
|
||||
// onSelect: '{{ onSelect }}',
|
||||
},
|
||||
},
|
||||
'Menu.Item': {
|
||||
type: 'void',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
'x-component': 'Menu.Item',
|
||||
},
|
||||
'Menu.Link': {
|
||||
type: 'void',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
'x-component': 'Menu.Link',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
name: uid(),
|
||||
'x-component': 'Grid',
|
||||
properties: {
|
||||
[`row_${uid()}`]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-component-props': {
|
||||
locked: true,
|
||||
},
|
||||
properties: {
|
||||
[`col_${uid()}`]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'AddNew.BlockItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
'Menu.URL': {
|
||||
type: 'void',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
'x-component': 'Menu.URL',
|
||||
},
|
||||
'Menu.SubMenu': {
|
||||
type: 'void',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
'x-component': 'Menu.SubMenu',
|
||||
},
|
||||
'Menu.Action': {
|
||||
type: 'void',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
'x-component': 'Menu.Action',
|
||||
},
|
||||
};
|
||||
|
||||
export default defaultSchemas;
|
@ -35,21 +35,21 @@ import cls from 'classnames';
|
||||
import { useDesignable } from '../../components/schema-renderer';
|
||||
import { MenuOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { IconPicker } from '../../components/icon-picker';
|
||||
import { useDefaultAction, VisibleContext } from '..';
|
||||
import { createSchema, removeSchema, useDefaultAction, VisibleContext } from '..';
|
||||
import { useMount } from 'ahooks';
|
||||
import './style.less';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useSchemaPath } from '@nocobase/client/lib';
|
||||
import { findPropertyByPath, useSchemaPath } from '@nocobase/client/lib';
|
||||
import { request } from '../';
|
||||
import defaultSchemas from './defaultSchemas';
|
||||
import { get } from 'lodash';
|
||||
|
||||
export const MenuModeContext = createContext(null);
|
||||
|
||||
function useSelectedKey() {}
|
||||
|
||||
const SideMenu = (props: any) => {
|
||||
const { visible, selectedKey, onSelect, path } = props;
|
||||
const { selectedKey, onSelect, path } = props;
|
||||
const { schema } = useDesignable();
|
||||
if (!selectedKey || !visible) {
|
||||
if (!selectedKey) {
|
||||
return null;
|
||||
}
|
||||
const child = schema.properties && schema.properties[selectedKey];
|
||||
@ -108,7 +108,9 @@ export const Menu: any = observer((props: any) => {
|
||||
if (mode === 'mix') {
|
||||
setSelectedKey(info.key);
|
||||
}
|
||||
onSelect && onSelect(info);
|
||||
const selectedSchema = schema.properties[info.key];
|
||||
console.log({ selectedSchema })
|
||||
onSelect && onSelect({ ...info, schema: selectedSchema });
|
||||
}}
|
||||
>
|
||||
<RecursionField schema={schema} onlyRenderProperties />
|
||||
@ -120,8 +122,12 @@ export const Menu: any = observer((props: any) => {
|
||||
<div ref={ref}>
|
||||
<SideMenu
|
||||
path={path}
|
||||
onSelect={onSelect}
|
||||
visible={mode === 'mix'}
|
||||
onSelect={(info) => {
|
||||
const keyPath = [selectedKey, ...info.keyPath];
|
||||
const selectedSchema = findPropertyByPath(schema, keyPath);
|
||||
console.log('keyPath', keyPath, selectedSchema);
|
||||
onSelect && onSelect({ ...info, keyPath, schema: selectedSchema });
|
||||
}}
|
||||
selectedKey={selectedKey}
|
||||
sideMenuRef={sideMenuRef}
|
||||
/>
|
||||
@ -131,62 +137,6 @@ export const Menu: any = observer((props: any) => {
|
||||
);
|
||||
});
|
||||
|
||||
Menu.AddNew = observer((props: any) => {
|
||||
const { appendChild } = useDesignable(props.path);
|
||||
return (
|
||||
<AntdMenu.ItemGroup
|
||||
className={'nb-menu-add-new'}
|
||||
title={
|
||||
<Dropdown
|
||||
overlay={
|
||||
<AntdMenu>
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const data = appendChild({
|
||||
type: 'void',
|
||||
title: uid(),
|
||||
'x-component': 'Menu.Item',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
});
|
||||
if (data['key']) {
|
||||
await request('ui_schemas:create', {
|
||||
method: 'post',
|
||||
data: data.toJSON(),
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
新建菜单
|
||||
</AntdMenu.Item>
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const data = appendChild({
|
||||
type: 'void',
|
||||
key: uid(),
|
||||
title: uid(),
|
||||
'x-component': 'Menu.SubMenu',
|
||||
'x-designable-bar': 'Menu.DesignableBar',
|
||||
});
|
||||
if (data['key']) {
|
||||
await request('ui_schemas:create', {
|
||||
method: 'post',
|
||||
data: data.toJSON(),
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
新建菜单组
|
||||
</AntdMenu.Item>
|
||||
</AntdMenu>
|
||||
}
|
||||
>
|
||||
<a>{props.children}</a>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Menu.Divider = observer(AntdMenu.Divider);
|
||||
|
||||
Menu.Item = observer((props: any) => {
|
||||
@ -288,6 +238,47 @@ Menu.SubMenu = observer((props: any) => {
|
||||
);
|
||||
});
|
||||
|
||||
Menu.AddNew = observer((props: any) => {
|
||||
const { appendChild } = useDesignable(props.path);
|
||||
return (
|
||||
<AntdMenu.ItemGroup
|
||||
className={'nb-menu-add-new'}
|
||||
title={
|
||||
<Dropdown
|
||||
overlay={
|
||||
<AntdMenu>
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const data = appendChild({
|
||||
...defaultSchemas['Menu.Link'],
|
||||
title: uid(),
|
||||
});
|
||||
await createSchema(data);
|
||||
}}
|
||||
>
|
||||
新建菜单
|
||||
</AntdMenu.Item>
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const data = appendChild({
|
||||
...defaultSchemas['Menu.SubMenu'],
|
||||
title: uid(),
|
||||
});
|
||||
await createSchema(data);
|
||||
}}
|
||||
>
|
||||
新建菜单组
|
||||
</AntdMenu.Item>
|
||||
</AntdMenu>
|
||||
}
|
||||
>
|
||||
<a>{props.children}</a>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Menu.DesignableBar = (props) => {
|
||||
const field = useField();
|
||||
const [visible, setVisible] = useState(false);
|
||||
@ -328,51 +319,32 @@ Menu.DesignableBar = (props) => {
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const s = insertAfter({
|
||||
type: 'void',
|
||||
...defaultSchemas['Menu.Link'],
|
||||
title: uid(),
|
||||
'x-component': 'Menu.SubMenu',
|
||||
});
|
||||
console.log('s.s.s.s', s);
|
||||
if (s['key']) {
|
||||
await request('ui_schemas:create', {
|
||||
method: 'post',
|
||||
data: s.toJSON(),
|
||||
});
|
||||
}
|
||||
await createSchema(s);
|
||||
}}
|
||||
>
|
||||
新建分组
|
||||
新建菜单项
|
||||
</AntdMenu.Item>
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const s = insertAfter({
|
||||
type: 'void',
|
||||
...defaultSchemas['Menu.SubMenu'],
|
||||
title: uid(),
|
||||
'x-component': 'Menu.Item',
|
||||
});
|
||||
if (s['key']) {
|
||||
await request('ui_schemas:create', {
|
||||
method: 'post',
|
||||
data: s.toJSON(),
|
||||
});
|
||||
}
|
||||
await createSchema(s);
|
||||
}}
|
||||
>
|
||||
新建菜单
|
||||
新建菜单分组
|
||||
</AntdMenu.Item>
|
||||
<AntdMenu.Item
|
||||
onClick={async () => {
|
||||
const s = appendChild({
|
||||
type: 'void',
|
||||
...defaultSchemas['Menu.Link'],
|
||||
title: uid(),
|
||||
'x-component': 'Menu.Item',
|
||||
});
|
||||
if (s['key']) {
|
||||
await request('ui_schemas:create', {
|
||||
method: 'post',
|
||||
data: s.toJSON(),
|
||||
});
|
||||
}
|
||||
await createSchema(s);
|
||||
}}
|
||||
>
|
||||
在菜单组里新增
|
||||
@ -382,8 +354,9 @@ Menu.DesignableBar = (props) => {
|
||||
Modal.confirm({
|
||||
title: '删除菜单',
|
||||
content: '确认删除此菜单项吗?',
|
||||
onOk: () => {
|
||||
remove();
|
||||
onOk: async () => {
|
||||
const target = remove();
|
||||
await removeSchema(target);
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
@ -1,35 +0,0 @@
|
||||
import { Model, ModelCtor } from '@nocobase/database';
|
||||
import { actions } from '@nocobase/actions';
|
||||
|
||||
export default async (ctx: actions.Context, next: actions.Next) => {
|
||||
const { resourceKey, filter } = ctx.action.params;
|
||||
const UISchema = ctx.db.getModel('ui_schemas');
|
||||
if (resourceKey) {
|
||||
const schema = await UISchema.findByPk(resourceKey);
|
||||
const property = schema.toProperty();
|
||||
const properties = await schema.getProperties();
|
||||
if (Object.keys(properties).length) {
|
||||
property.properties = properties;
|
||||
}
|
||||
ctx.body = property;
|
||||
} else {
|
||||
const schemas = await UISchema.findAll(UISchema.parseApiJson({
|
||||
filter,
|
||||
}));
|
||||
let properties = {};
|
||||
for (const schema of schemas) {
|
||||
const property = schema.toProperty();
|
||||
const properties = await schema.getProperties();
|
||||
if (Object.keys(properties).length) {
|
||||
property.properties = properties;
|
||||
}
|
||||
properties[property.name] = property;
|
||||
}
|
||||
ctx.body = {
|
||||
type: 'object',
|
||||
properties,
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
}
|
91
packages/plugin-ui-schema/src/actions/index.ts
Normal file
91
packages/plugin-ui-schema/src/actions/index.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { Model, ModelCtor } from '@nocobase/database';
|
||||
import { actions, middlewares } from '@nocobase/actions';
|
||||
import { sort } from '@nocobase/actions/src/actions/common';
|
||||
import { cloneDeep, omit } from 'lodash';
|
||||
|
||||
export const create = async (ctx: actions.Context, next: actions.Next) => {
|
||||
const values = cloneDeep(ctx.action.params.values);
|
||||
ctx.action.mergeParams(
|
||||
{
|
||||
values: omit(values, [
|
||||
'__insertAfter__',
|
||||
'__insertBefore__',
|
||||
'__prepend__',
|
||||
'_isJSONSchemaObject',
|
||||
]),
|
||||
},
|
||||
{
|
||||
payload: 'replace',
|
||||
},
|
||||
);
|
||||
await actions.common.create(ctx, async () => {});
|
||||
const targetKey = values['__insertAfter__'] || values['__insertBefore__'];
|
||||
if (targetKey) {
|
||||
console.log({
|
||||
associatedKey: values.parentKey,
|
||||
resourceKey: ctx.body.key,
|
||||
values: {
|
||||
field: 'sort',
|
||||
target: {
|
||||
key: targetKey,
|
||||
},
|
||||
},
|
||||
});
|
||||
ctx.action.mergeParams(
|
||||
{
|
||||
associatedKey: values.parentKey,
|
||||
resourceKey: ctx.body.key,
|
||||
values: {
|
||||
field: 'sort',
|
||||
insertAfter: !!values['__insertAfter__'],
|
||||
target: {
|
||||
key: targetKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
payload: 'replace',
|
||||
},
|
||||
);
|
||||
await middlewares.associated(ctx, async () => {});
|
||||
await sort(ctx, async () => {});
|
||||
}
|
||||
await next();
|
||||
};
|
||||
|
||||
export const getTree = async (ctx: actions.Context, next: actions.Next) => {
|
||||
const { resourceKey, filter } = ctx.action.params;
|
||||
const UISchema = ctx.db.getModel('ui_schemas');
|
||||
if (resourceKey) {
|
||||
const schema = await UISchema.findByPk(resourceKey);
|
||||
const property = schema.toProperty();
|
||||
const properties = await schema.getProperties();
|
||||
if (Object.keys(properties).length) {
|
||||
property.properties = properties;
|
||||
}
|
||||
ctx.body = property;
|
||||
} else {
|
||||
const schemas = await UISchema.findAll(
|
||||
UISchema.parseApiJson({
|
||||
filter,
|
||||
sort: ['sort'],
|
||||
}),
|
||||
);
|
||||
console.log({ schemas });
|
||||
let properties = {};
|
||||
for (const schema of schemas) {
|
||||
const property = schema.toProperty();
|
||||
const childProperties = await schema.getProperties();
|
||||
if (Object.keys(childProperties).length) {
|
||||
property.properties = childProperties;
|
||||
}
|
||||
properties[property.name] = property;
|
||||
}
|
||||
ctx.body = {
|
||||
type: 'object',
|
||||
properties,
|
||||
};
|
||||
}
|
||||
|
||||
await next();
|
||||
};
|
@ -10,6 +10,11 @@ export default {
|
||||
name: 'key',
|
||||
primaryKey: true,
|
||||
},
|
||||
{
|
||||
type: 'sort',
|
||||
name: 'sort',
|
||||
scope: ['parentKey'],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
|
@ -7,6 +7,13 @@ export class UISchema extends Model {
|
||||
const attributes = this.toAttributes(value);
|
||||
// @ts-ignore
|
||||
const model: Model = await super.create(attributes, options);
|
||||
if (value['__prepend__']) {
|
||||
console.log('__prepend__', model.parentKey);
|
||||
} else if (value['__insertAfter__']) {
|
||||
console.log('__insertAfter__', model.parentKey);
|
||||
} else if (value['__insertBefore__']) {
|
||||
console.log('__insertBefore__', model.parentKey);
|
||||
}
|
||||
if (!attributes.children) {
|
||||
attributes.children = this.properties2children(attributes.properties);
|
||||
await model.updateAssociation('children', attributes.children, options);
|
||||
@ -48,7 +55,9 @@ export class UISchema extends Model {
|
||||
|
||||
async getProperties() {
|
||||
const properties = {};
|
||||
const children: UISchema[] = await this.getChildren();
|
||||
const children: UISchema[] = await this.getChildren({
|
||||
order: [['sort', 'asc']],
|
||||
});
|
||||
for (const child of children) {
|
||||
const property = child.toProperty();
|
||||
const childProperties = await child.getProperties();
|
||||
|
@ -2,7 +2,7 @@ import path from 'path';
|
||||
import { Application } from '@nocobase/server';
|
||||
import { registerModels, Table } from '@nocobase/database';
|
||||
import * as models from './models';
|
||||
import getTree from './actions/getTree';
|
||||
import { create, getTree } from './actions';
|
||||
|
||||
export default async function (this: Application, options = {}) {
|
||||
const database = this.database;
|
||||
@ -12,11 +12,6 @@ export default async function (this: Application, options = {}) {
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
// database.getModel('ui_schemas').beforeCreate((model) => {
|
||||
// if (!model.get('name')) {
|
||||
// model.set('name', model.get('key'));
|
||||
// }
|
||||
// });
|
||||
|
||||
this.resourcer.registerActionHandler('ui_schemas:create', create);
|
||||
this.resourcer.registerActionHandler('ui_schemas:getTree', getTree);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user