diff --git a/packages/actions/src/actions/common.ts b/packages/actions/src/actions/common.ts index d5afaf2d1..de2e4233c 100644 --- a/packages/actions/src/actions/common.ts +++ b/packages/actions/src/actions/common.ts @@ -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, { diff --git a/packages/client/src/components/admin-layout/DataTableConfig/index.tsx b/packages/client/src/components/admin-layout/DataTableConfig/index.tsx new file mode 100644 index 000000000..6a63344a0 --- /dev/null +++ b/packages/client/src/components/admin-layout/DataTableConfig/index.tsx @@ -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 ( + <> + + + { + form.setValues({ + title: `数据表${info.key}`, + }); + }} + > + 数据表1 + 数据表2 + + 新建数据表 + + } + > + 数据表3 + + + } + visible={visible} + onOk={async () => { + await form.submit(); + setVisible(false); + }} + onCancel={() => { + setVisible(false); + }} + > + + + + ); +}; diff --git a/packages/client/src/components/admin-layout/DataTableConfig/schema.ts b/packages/client/src/components/admin-layout/DataTableConfig/schema.ts new file mode 100644 index 000000000..66125906f --- /dev/null +++ b/packages/client/src/components/admin-layout/DataTableConfig/schema.ts @@ -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; \ No newline at end of file diff --git a/packages/client/src/components/admin-layout/index.tsx b/packages/client/src/components/admin-layout/index.tsx index 79b185b3b..1258cc3aa 100644 --- a/packages/client/src/components/admin-layout/index.tsx +++ b/packages/client/src/components/admin-layout/index.tsx @@ -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(); @@ -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), }} /> - + ; } - 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 ; + return ; } export function AdminLayout({ route }: any) { diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx index d91b4814d..db8a78a30 100644 --- a/packages/client/src/components/schema-renderer/index.tsx +++ b/packages/client/src/components/schema-renderer/index.tsx @@ -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; diff --git a/packages/client/src/demos/demo1.tsx b/packages/client/src/demos/demo1.tsx index 71abaa3b4..ce9d6b7ed 100644 --- a/packages/client/src/demos/demo1.tsx +++ b/packages/client/src/demos/demo1.tsx @@ -38,7 +38,7 @@ const App = () => { return (
- +
diff --git a/packages/client/src/schemas/add-new/index.tsx b/packages/client/src/schemas/add-new/index.tsx index f2b242d8c..d47e69bd0 100644 --- a/packages/client/src/schemas/add-new/index.tsx +++ b/packages/client/src/schemas/add-new/index.tsx @@ -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 ( { 新增数据表 { + 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); } }} > diff --git a/packages/client/src/schemas/grid/index.tsx b/packages/client/src/schemas/grid/index.tsx index 13cc56895..1deab02e3 100644 --- a/packages/client/src/schemas/grid/index.tsx +++ b/packages/client/src/schemas/grid/index.tsx @@ -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) => { { + 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) => { { + 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) => { { + 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) => { { + 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) => { ); }); - -// 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 ( -// -//
-// {props.children} -//
-//
-// ); -// }); diff --git a/packages/client/src/schemas/index.tsx b/packages/client/src/schemas/index.tsx index 0a371888b..e95be4799 100644 --- a/packages/client/src/schemas/index.tsx +++ b/packages/client/src/schemas/index.tsx @@ -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'], + } + }, + }); +} diff --git a/packages/client/src/schemas/markdown/index.tsx b/packages/client/src/schemas/markdown/index.tsx index 33be81d82..797985fdb 100644 --- a/packages/client/src/schemas/markdown/index.tsx +++ b/packages/client/src/schemas/markdown/index.tsx @@ -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 + }, mapProps((props: any, field) => { return { ...props, diff --git a/packages/client/src/schemas/menu/defaultSchemas.ts b/packages/client/src/schemas/menu/defaultSchemas.ts new file mode 100644 index 000000000..dc9fa6e30 --- /dev/null +++ b/packages/client/src/schemas/menu/defaultSchemas.ts @@ -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; diff --git a/packages/client/src/schemas/menu/index.tsx b/packages/client/src/schemas/menu/index.tsx index 15ddb5095..d26d227bd 100644 --- a/packages/client/src/schemas/menu/index.tsx +++ b/packages/client/src/schemas/menu/index.tsx @@ -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 }); }} > @@ -120,8 +122,12 @@ export const Menu: any = observer((props: any) => {
{ + 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 ( - - { - 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(), - }); - } - }} - > - 新建菜单 - - { - 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(), - }); - } - }} - > - 新建菜单组 - - - } - > - {props.children} - - } - /> - ); -}); - 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 ( + + { + const data = appendChild({ + ...defaultSchemas['Menu.Link'], + title: uid(), + }); + await createSchema(data); + }} + > + 新建菜单 + + { + const data = appendChild({ + ...defaultSchemas['Menu.SubMenu'], + title: uid(), + }); + await createSchema(data); + }} + > + 新建菜单组 + + + } + > + {props.children} + + } + /> + ); +}); + Menu.DesignableBar = (props) => { const field = useField(); const [visible, setVisible] = useState(false); @@ -328,51 +319,32 @@ Menu.DesignableBar = (props) => { { 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); }} > - 新建分组 + 新建菜单项 { 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); }} > - 新建菜单 + 新建菜单分组 { 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); }, }); }} diff --git a/packages/plugin-ui-schema/src/actions/getTree.ts b/packages/plugin-ui-schema/src/actions/getTree.ts deleted file mode 100644 index 7c207cd5b..000000000 --- a/packages/plugin-ui-schema/src/actions/getTree.ts +++ /dev/null @@ -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(); -} diff --git a/packages/plugin-ui-schema/src/actions/index.ts b/packages/plugin-ui-schema/src/actions/index.ts new file mode 100644 index 000000000..3f0e0985a --- /dev/null +++ b/packages/plugin-ui-schema/src/actions/index.ts @@ -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(); +}; diff --git a/packages/plugin-ui-schema/src/collections/ui_schemas.ts b/packages/plugin-ui-schema/src/collections/ui_schemas.ts index 4cce6c1bb..6783aaef6 100644 --- a/packages/plugin-ui-schema/src/collections/ui_schemas.ts +++ b/packages/plugin-ui-schema/src/collections/ui_schemas.ts @@ -10,6 +10,11 @@ export default { name: 'key', primaryKey: true, }, + { + type: 'sort', + name: 'sort', + scope: ['parentKey'], + }, { type: 'string', name: 'name', diff --git a/packages/plugin-ui-schema/src/models/ui-schema.ts b/packages/plugin-ui-schema/src/models/ui-schema.ts index b1db0f3ca..15d4bc610 100644 --- a/packages/plugin-ui-schema/src/models/ui-schema.ts +++ b/packages/plugin-ui-schema/src/models/ui-schema.ts @@ -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(); diff --git a/packages/plugin-ui-schema/src/server.ts b/packages/plugin-ui-schema/src/server.ts index 8318f9ff4..c0f74088b 100644 --- a/packages/plugin-ui-schema/src/server.ts +++ b/packages/plugin-ui-schema/src/server.ts @@ -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); }