From 63538cf240a615bd756f9e2591b874aafc3ab35f Mon Sep 17 00:00:00 2001 From: chenos Date: Sun, 18 Jul 2021 17:16:57 +0800 Subject: [PATCH] updates... --- .../admin-layout/DataTableConfig/index.tsx | 68 ------- .../admin-layout/DataTableConfig/schema.ts | 49 ----- .../src/components/admin-layout/datatable.ts | 56 ------ .../src/components/admin-layout/datatable.tsx | 45 +++++ .../src/components/admin-layout/index.tsx | 4 +- .../src/components/admin-layout/style.less | 28 +-- .../src/components/schema-renderer/index.tsx | 3 +- .../schemas/database-field/demos/demo2.tsx | 83 ++++---- .../src/schemas/database-field/index.tsx | 187 ++++++++++++++++-- .../src/schemas/database-field/style.less | 3 + packages/client/src/schemas/grid/index.tsx | 34 +++- packages/client/src/schemas/index.tsx | 17 ++ .../plugin-collections/src/actions/index.ts | 24 +++ packages/plugin-collections/src/server.ts | 3 + .../plugin-ui-schema/src/actions/index.ts | 21 +- 15 files changed, 364 insertions(+), 261 deletions(-) delete mode 100644 packages/client/src/components/admin-layout/DataTableConfig/index.tsx delete mode 100644 packages/client/src/components/admin-layout/DataTableConfig/schema.ts delete mode 100644 packages/client/src/components/admin-layout/datatable.ts create mode 100644 packages/client/src/components/admin-layout/datatable.tsx create mode 100644 packages/client/src/schemas/database-field/style.less create mode 100644 packages/plugin-collections/src/actions/index.ts diff --git a/packages/client/src/components/admin-layout/DataTableConfig/index.tsx b/packages/client/src/components/admin-layout/DataTableConfig/index.tsx deleted file mode 100644 index 6a63344a0..000000000 --- a/packages/client/src/components/admin-layout/DataTableConfig/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -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 deleted file mode 100644 index 66125906f..000000000 --- a/packages/client/src/components/admin-layout/DataTableConfig/schema.ts +++ /dev/null @@ -1,49 +0,0 @@ -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/datatable.ts b/packages/client/src/components/admin-layout/datatable.ts deleted file mode 100644 index 7b8dde280..000000000 --- a/packages/client/src/components/admin-layout/datatable.ts +++ /dev/null @@ -1,56 +0,0 @@ - -export const schema = { - type: 'void', - name: 'action1', - // title: 'ModalForm', - 'x-component': 'Action', - 'x-component-props': { - type: 'primary', - icon: 'DatabaseOutlined', - }, - properties: { - modal1: { - type: 'void', - title: '对话框标题', - 'x-decorator': 'Form', - 'x-component': 'Action.Modal', - 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: '内容', - }, - }, - ], - }, - }, - }, - }, -}; - -export default schema; diff --git a/packages/client/src/components/admin-layout/datatable.tsx b/packages/client/src/components/admin-layout/datatable.tsx new file mode 100644 index 000000000..177a0db47 --- /dev/null +++ b/packages/client/src/components/admin-layout/datatable.tsx @@ -0,0 +1,45 @@ +import { SchemaRenderer } from '@nocobase/client/lib'; +import React from 'react'; + +export default () => { + const schema = { + type: 'array', + name: 'collections', + 'x-component': 'DatabaseCollection', + 'x-component-props': {}, + default: [ + { + name: 'test1', + title: '数据表 1', + }, + { + name: 'test2', + title: '数据表 2', + }, + ], + 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: [], + }, + }, + }; + return ; +}; diff --git a/packages/client/src/components/admin-layout/index.tsx b/packages/client/src/components/admin-layout/index.tsx index 1258cc3aa..fa3efbf7a 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 DataTableConfig from './DataTableConfig'; +import Database from './datatable'; function LayoutWithMenu({ schema }) { const match = useRouteMatch(); @@ -53,7 +53,7 @@ function LayoutWithMenu({ schema }) { selectedKeys: [activeKey].filter(Boolean), }} /> - + .ant-collapse-item { - border-bottom: 1px solid #d9d9d9 !important; - } - .ant-collapse-content { - border-top: 1px solid #d9d9d9 !important; - } - .ant-collapse-content > .ant-collapse-content-box { - padding: 24px !important; - padding-bottom: 0 !important; - } -} +// .ant-collapse { +// border: 1px solid #d9d9d9 !important; +// border-bottom: 0 !important; +// > .ant-collapse-item { +// border-bottom: 1px solid #d9d9d9 !important; +// } +// .ant-collapse-content { +// border-top: 1px solid #d9d9d9 !important; +// } +// .ant-collapse-content > .ant-collapse-content-box { +// padding: 24px !important; +// padding-bottom: 0 !important; +// } +// } .database-sider { background: #fafafa; diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx index db8a78a30..60bfeef9e 100644 --- a/packages/client/src/components/schema-renderer/index.tsx +++ b/packages/client/src/components/schema-renderer/index.tsx @@ -27,7 +27,7 @@ import { AddNew } from '../../schemas/add-new'; import { Cascader } from '../../schemas/cascader'; import { Checkbox } from '../../schemas/checkbox'; import { ColorSelect } from '../../schemas/color-select'; -import { DatabaseField } from '../../schemas/database-field'; +import { DatabaseField, DatabaseCollection } from '../../schemas/database-field'; import { DatePicker } from '../../schemas/date-picker'; import { Filter } from '../../schemas/filter'; import { Form } from '../../schemas/form'; @@ -79,6 +79,7 @@ export const SchemaField = createSchemaField({ Checkbox, ColorSelect, DatabaseField, + DatabaseCollection, DatePicker, Filter, Form, diff --git a/packages/client/src/schemas/database-field/demos/demo2.tsx b/packages/client/src/schemas/database-field/demos/demo2.tsx index 79542b800..03ce7b0db 100644 --- a/packages/client/src/schemas/database-field/demos/demo2.tsx +++ b/packages/client/src/schemas/database-field/demos/demo2.tsx @@ -1,55 +1,46 @@ import React from 'react'; import { createForm } from '@formily/core'; import { SchemaRenderer } from '../../'; -import { observer, connect, useField } from '@formily/react'; +import { observer, connect, useField, ISchema } from '@formily/react'; -const schema = { - type: 'void', - name: 'action1', - title: 'ModalForm', - 'x-component': 'Action', +const schema: ISchema = { + type: 'array', + name: 'collections', + 'x-component': 'DatabaseCollection', 'x-component-props': {}, - properties: { - modal1: { - type: 'void', - title: '对话框标题', - 'x-decorator': 'Form', - 'x-component': 'Action.Modal', - properties: { - title: { - type: 'string', - title: '数据表名称', - required: true, - 'x-decorator': 'FormItem', - 'x-component': 'Input', - }, - fields: { - type: 'array', - 'x-component': 'DatabaseField', - default: [ - { - id: 1, - interface: 'string', - dataType: 'string', - name: 'title', - ui: { - title: '标题', - }, - }, - { - id: 2, - dataType: 'text', - interface: 'textarea', - name: 'content', - ui: { - title: '内容', - }, - }, - ], - }, - }, + default: [ + { + name: 'test1', + title: '数据表 1', }, - }, + { + name: 'test2', + title: '数据表 2', + } + ], + 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: [], + } + } }; const form = createForm(); diff --git a/packages/client/src/schemas/database-field/index.tsx b/packages/client/src/schemas/database-field/index.tsx index 53585d7f4..c919ee689 100644 --- a/packages/client/src/schemas/database-field/index.tsx +++ b/packages/client/src/schemas/database-field/index.tsx @@ -7,17 +7,177 @@ import { ISchema, Schema, useFieldSchema, + useForm, } from '@formily/react'; -import { ArrayCollapse } from '@formily/antd'; +import { ArrayCollapse, FormLayout } from '@formily/antd'; import { uid } from '@formily/shared'; import '@formily/antd/lib/form-tab/style'; -import { Collapse, Button, Dropdown, Menu, Tag } from 'antd'; +import { + Collapse, + Button, + Dropdown, + Menu, + Tag, + Select, + Divider, + Input, +} from 'antd'; import { options, interfaces } from './interfaces'; -import { DeleteOutlined } from '@ant-design/icons'; +import { + DeleteOutlined, + DatabaseOutlined, + PlusOutlined, + CloseOutlined, +} from '@ant-design/icons'; +import cls from 'classnames'; +import './style.less'; +import Modal from 'antd/lib/modal/Modal'; +import { get } from 'lodash'; +import { useEffect } from 'react'; +import { useRequest } from 'ahooks'; +import { createOrUpdateCollection, deleteCollection } from '..'; + +export const DatabaseCollection = observer((props) => { + const field = useField(); + const [visible, setVisible] = useState(false); + const [open, setOpen] = useState(false); + const schema = useFieldSchema(); + const [activeIndex, setActiveIndex] = useState(0); + const form = useForm(); + const [newValue, setNewValue] = useState(''); + + useRequest('collections:list?sort=-created_at', { + formatResult: (result) => result?.data, + onSuccess(data) { + field.setValue(data); + console.log('onSuccess', data); + }, + }); + + return ( +
+ + + +
+ } + visible={visible} + onCancel={() => { + setVisible(false); + }} + onOk={async () => { + try { + form.clearErrors(); + await form.validate(`${field.address.entire}.${activeIndex}.*`); + setVisible(false); + await createOrUpdateCollection(field.value[activeIndex]); + console.log( + `${field.address.entire}.${activeIndex}.*`, + field.value[activeIndex], + ); + } catch (error) {} + }} + > + + + + + + ); +}); export const DatabaseField: any = observer((props) => { const field = useField(); - console.log('DatabaseField', field.componentProps); + console.log('DatabaseField', field.value); const [activeKey, setActiveKey] = useState(null); return (
@@ -26,6 +186,7 @@ export const DatabaseField: any = observer((props) => { onChange={(key) => { setActiveKey(key); }} + className={cls({ empty: !field.value?.length })} accordion > {field.value?.map((item, index) => { @@ -35,9 +196,13 @@ export const DatabaseField: any = observer((props) => { - {(item.ui && item.ui.title) || 未命名}{' '} + {(item.ui && item.ui.title) || ( + 未命名 + )}{' '} {schema.title} - {item.name} + + {item.name} + } extra={[ @@ -97,13 +262,11 @@ export const DatabaseField: any = observer((props) => { console.log('info.key', info.key, schema); }} > - {options.map(option => ( + {options.map((option) => ( - { - option.children.map(item => ( - {item.title} - )) - } + {option.children.map((item) => ( + {item.title} + ))} ))} diff --git a/packages/client/src/schemas/database-field/style.less b/packages/client/src/schemas/database-field/style.less new file mode 100644 index 000000000..68535f5fa --- /dev/null +++ b/packages/client/src/schemas/database-field/style.less @@ -0,0 +1,3 @@ +.ant-collapse.empty { + border: 0; +} diff --git a/packages/client/src/schemas/grid/index.tsx b/packages/client/src/schemas/grid/index.tsx index 1deab02e3..c881de031 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 { createSchema, useDesignable, useSchemaPath } from '../'; +import { createSchema, removeSchema, useDesignable, useSchemaPath } from '../'; import { useDrag, useDrop, @@ -37,6 +37,10 @@ export const GridBlockContext = createContext({ dragRef: null, }); +function isGridRowOrCol(schema: ISchema) { + return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']); +} + const RowDivider = ({ name, onDrop }) => { const uid = useDragDropUID(); const { isOver, dropRef } = useDrop({ @@ -111,7 +115,11 @@ export const Grid: any = observer((props) => { }); await createSchema(data); console.log('prepend', data); - deepRemove(path); + const removed = deepRemove(path); + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } }} /> {schema.mapProperties((property, key, index) => { @@ -143,8 +151,12 @@ export const Grid: any = observer((props) => { [...gridPath, key], ); await createSchema(data); - console.log('insertAfter', data); - deepRemove(path); + const removed = deepRemove(path); + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } + console.log('insertAfter', data, removed); }} /> @@ -185,7 +197,11 @@ Grid.Row = observer((props) => { }); await createSchema(data); const path = [...e.dragItem.path]; - deepRemove(path); + const removed = deepRemove(path); + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } }} /> {schema.mapProperties((property, key, index) => { @@ -207,10 +223,14 @@ Grid.Row = observer((props) => { }, [...rowPath, key], ); - console.log('ColDivider', data) + console.log('ColDivider', data); await createSchema(data); const path = [...e.dragItem.path]; - deepRemove(path); + const removed = deepRemove(path); + const last = removed.pop(); + if (isGridRowOrCol(last)) { + await removeSchema(last); + } }} onDragEnd={(e) => { designableSchema.mapProperties((s, key, index) => { diff --git a/packages/client/src/schemas/index.tsx b/packages/client/src/schemas/index.tsx index e95be4799..abaebe818 100644 --- a/packages/client/src/schemas/index.tsx +++ b/packages/client/src/schemas/index.tsx @@ -18,6 +18,23 @@ export const request = extend({ timeout: 1000, }); +export async function createOrUpdateCollection(data: any) { + return await request('collections:createOrUpdate', { + method: 'post', + data, + }); +} + +export async function deleteCollection(name) { + await request('collections:destroy', { + method: 'post', + params: { + filter: { + name, + } + }, + }); +} export async function createSchema(schema: ISchema) { if (!schema['key']) { diff --git a/packages/plugin-collections/src/actions/index.ts b/packages/plugin-collections/src/actions/index.ts new file mode 100644 index 000000000..da68068e9 --- /dev/null +++ b/packages/plugin-collections/src/actions/index.ts @@ -0,0 +1,24 @@ +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 createOrUpdate = async (ctx: actions.Context, next: actions.Next) => { + const { values } = ctx.action.params; + const Collection = ctx.db.getModel('collections'); + let collection; + if (values.name) { + collection = await Collection.findByPk(values.name); + } + try { + if (!collection) { + collection = await Collection.create(values); + } else { + await collection.update(values); + } + } catch (error) { + console.log('error.errors', error.errors) + } + + ctx.body = collection; +} \ No newline at end of file diff --git a/packages/plugin-collections/src/server.ts b/packages/plugin-collections/src/server.ts index 9519b62ad..6a8acecba 100644 --- a/packages/plugin-collections/src/server.ts +++ b/packages/plugin-collections/src/server.ts @@ -2,6 +2,7 @@ import path from 'path'; import { Application } from '@nocobase/server'; import { registerModels, Table } from '@nocobase/database'; import * as models from './models'; +import { createOrUpdate } from './actions'; export default async function (this: Application, options = {}) { const database = this.database; @@ -16,4 +17,6 @@ export default async function (this: Application, options = {}) { model.set('name', model.get('key')); } }); + + this.resourcer.registerActionHandler('collections:createOrUpdate', createOrUpdate); } diff --git a/packages/plugin-ui-schema/src/actions/index.ts b/packages/plugin-ui-schema/src/actions/index.ts index 3f0e0985a..0e513ea31 100644 --- a/packages/plugin-ui-schema/src/actions/index.ts +++ b/packages/plugin-ui-schema/src/actions/index.ts @@ -18,14 +18,19 @@ export const create = async (ctx: actions.Context, next: actions.Next) => { payload: 'replace', }, ); - await actions.common.create(ctx, async () => {}); + await actions.common.create(ctx, async () => { }); + const sticky = values['__prepend__']; const targetKey = values['__insertAfter__'] || values['__insertBefore__']; - if (targetKey) { + if (sticky || targetKey) { console.log({ associatedKey: values.parentKey, resourceKey: ctx.body.key, - values: { + values: sticky ? { field: 'sort', + sticky: true, + } : { + field: 'sort', + insertAfter: !!values['__insertAfter__'], target: { key: targetKey, }, @@ -35,7 +40,11 @@ export const create = async (ctx: actions.Context, next: actions.Next) => { { associatedKey: values.parentKey, resourceKey: ctx.body.key, - values: { + values: sticky ? { + field: 'sort', + sticky: true, + target: {}, + } : { field: 'sort', insertAfter: !!values['__insertAfter__'], target: { @@ -47,8 +56,8 @@ export const create = async (ctx: actions.Context, next: actions.Next) => { payload: 'replace', }, ); - await middlewares.associated(ctx, async () => {}); - await sort(ctx, async () => {}); + await middlewares.associated(ctx, async () => { }); + await sort(ctx, async () => { }); } await next(); };