updates...
This commit is contained in:
parent
057dc8406a
commit
63538cf240
@ -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 (
|
|
||||||
<>
|
|
||||||
<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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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;
|
|
@ -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;
|
|
45
packages/client/src/components/admin-layout/datatable.tsx
Normal file
45
packages/client/src/components/admin-layout/datatable.tsx
Normal file
@ -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 <SchemaRenderer schema={schema} />;
|
||||||
|
};
|
@ -23,7 +23,7 @@ import './style.less';
|
|||||||
|
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import DataTableConfig from './DataTableConfig';
|
import Database from './datatable';
|
||||||
|
|
||||||
function LayoutWithMenu({ schema }) {
|
function LayoutWithMenu({ schema }) {
|
||||||
const match = useRouteMatch<any>();
|
const match = useRouteMatch<any>();
|
||||||
@ -53,7 +53,7 @@ function LayoutWithMenu({ schema }) {
|
|||||||
selectedKeys: [activeKey].filter(Boolean),
|
selectedKeys: [activeKey].filter(Boolean),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<DataTableConfig />
|
<Database />
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Sider
|
<Layout.Sider
|
||||||
|
@ -18,20 +18,20 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
.ant-collapse {
|
// .ant-collapse {
|
||||||
border: 1px solid #d9d9d9 !important;
|
// border: 1px solid #d9d9d9 !important;
|
||||||
border-bottom: 0 !important;
|
// border-bottom: 0 !important;
|
||||||
> .ant-collapse-item {
|
// > .ant-collapse-item {
|
||||||
border-bottom: 1px solid #d9d9d9 !important;
|
// border-bottom: 1px solid #d9d9d9 !important;
|
||||||
}
|
// }
|
||||||
.ant-collapse-content {
|
// .ant-collapse-content {
|
||||||
border-top: 1px solid #d9d9d9 !important;
|
// border-top: 1px solid #d9d9d9 !important;
|
||||||
}
|
// }
|
||||||
.ant-collapse-content > .ant-collapse-content-box {
|
// .ant-collapse-content > .ant-collapse-content-box {
|
||||||
padding: 24px !important;
|
// padding: 24px !important;
|
||||||
padding-bottom: 0 !important;
|
// padding-bottom: 0 !important;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
.database-sider {
|
.database-sider {
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
|
@ -27,7 +27,7 @@ import { AddNew } from '../../schemas/add-new';
|
|||||||
import { Cascader } from '../../schemas/cascader';
|
import { Cascader } from '../../schemas/cascader';
|
||||||
import { Checkbox } from '../../schemas/checkbox';
|
import { Checkbox } from '../../schemas/checkbox';
|
||||||
import { ColorSelect } from '../../schemas/color-select';
|
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 { DatePicker } from '../../schemas/date-picker';
|
||||||
import { Filter } from '../../schemas/filter';
|
import { Filter } from '../../schemas/filter';
|
||||||
import { Form } from '../../schemas/form';
|
import { Form } from '../../schemas/form';
|
||||||
@ -79,6 +79,7 @@ export const SchemaField = createSchemaField({
|
|||||||
Checkbox,
|
Checkbox,
|
||||||
ColorSelect,
|
ColorSelect,
|
||||||
DatabaseField,
|
DatabaseField,
|
||||||
|
DatabaseCollection,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
Filter,
|
Filter,
|
||||||
Form,
|
Form,
|
||||||
|
@ -1,55 +1,46 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { SchemaRenderer } from '../../';
|
import { SchemaRenderer } from '../../';
|
||||||
import { observer, connect, useField } from '@formily/react';
|
import { observer, connect, useField, ISchema } from '@formily/react';
|
||||||
|
|
||||||
const schema = {
|
const schema: ISchema = {
|
||||||
type: 'void',
|
type: 'array',
|
||||||
name: 'action1',
|
name: 'collections',
|
||||||
title: 'ModalForm',
|
'x-component': 'DatabaseCollection',
|
||||||
'x-component': 'Action',
|
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
properties: {
|
default: [
|
||||||
modal1: {
|
{
|
||||||
type: 'void',
|
name: 'test1',
|
||||||
title: '对话框标题',
|
title: '数据表 1',
|
||||||
'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: '内容',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
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();
|
const form = createForm();
|
||||||
|
@ -7,17 +7,177 @@ import {
|
|||||||
ISchema,
|
ISchema,
|
||||||
Schema,
|
Schema,
|
||||||
useFieldSchema,
|
useFieldSchema,
|
||||||
|
useForm,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { ArrayCollapse } from '@formily/antd';
|
import { ArrayCollapse, FormLayout } from '@formily/antd';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import '@formily/antd/lib/form-tab/style';
|
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 { 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<Formily.Core.Models.ArrayField>();
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
type={'primary'}
|
||||||
|
>
|
||||||
|
<DatabaseOutlined />
|
||||||
|
</Button>
|
||||||
|
<Modal
|
||||||
|
title={
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<Select
|
||||||
|
value={activeIndex}
|
||||||
|
style={{ minWidth: 300, textAlign: 'center' }}
|
||||||
|
onChange={(value) => {
|
||||||
|
setActiveIndex(value as any);
|
||||||
|
}}
|
||||||
|
open={open}
|
||||||
|
onDropdownVisibleChange={setOpen}
|
||||||
|
optionLabelProp={'label'}
|
||||||
|
dropdownRender={(menu) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{menu}
|
||||||
|
<Divider style={{ margin: '5px 0 4px' }} />
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
padding: '5px 12px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input.Search
|
||||||
|
size={'middle'}
|
||||||
|
placeholder={'新增数据表'}
|
||||||
|
enterButton={<PlusOutlined />}
|
||||||
|
value={newValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
setNewValue(e.target.value);
|
||||||
|
}}
|
||||||
|
onSearch={async (value) => {
|
||||||
|
const data = {
|
||||||
|
name: uid(),
|
||||||
|
title: value,
|
||||||
|
};
|
||||||
|
field.push(data);
|
||||||
|
setActiveIndex(field.value.length - 1);
|
||||||
|
setOpen(false);
|
||||||
|
setNewValue('');
|
||||||
|
await createOrUpdateCollection(data);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{field.value?.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<Select.Option value={index} label={item.title || '未命名'}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.title || '未命名'}
|
||||||
|
<DeleteOutlined
|
||||||
|
onClick={async (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
field.remove(index);
|
||||||
|
if (activeIndex === index) {
|
||||||
|
setActiveIndex(0);
|
||||||
|
} else if (activeIndex > index) {
|
||||||
|
setActiveIndex(activeIndex - 1);
|
||||||
|
}
|
||||||
|
await deleteCollection(item.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Select.Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
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) {}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<RecursionField
|
||||||
|
name={activeIndex}
|
||||||
|
schema={
|
||||||
|
new Schema({
|
||||||
|
type: 'object',
|
||||||
|
properties: schema.properties,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export const DatabaseField: any = observer((props) => {
|
export const DatabaseField: any = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.ArrayField>();
|
const field = useField<Formily.Core.Models.ArrayField>();
|
||||||
console.log('DatabaseField', field.componentProps);
|
console.log('DatabaseField', field.value);
|
||||||
const [activeKey, setActiveKey] = useState(null);
|
const [activeKey, setActiveKey] = useState(null);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -26,6 +186,7 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
onChange={(key) => {
|
onChange={(key) => {
|
||||||
setActiveKey(key);
|
setActiveKey(key);
|
||||||
}}
|
}}
|
||||||
|
className={cls({ empty: !field.value?.length })}
|
||||||
accordion
|
accordion
|
||||||
>
|
>
|
||||||
{field.value?.map((item, index) => {
|
{field.value?.map((item, index) => {
|
||||||
@ -35,9 +196,13 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
<Collapse.Panel
|
<Collapse.Panel
|
||||||
header={
|
header={
|
||||||
<>
|
<>
|
||||||
{(item.ui && item.ui.title) || <i style={{color: 'rgba(0, 0, 0, 0.25)'}}>未命名</i>}{' '}
|
{(item.ui && item.ui.title) || (
|
||||||
|
<i style={{ color: 'rgba(0, 0, 0, 0.25)' }}>未命名</i>
|
||||||
|
)}{' '}
|
||||||
<Tag>{schema.title}</Tag>
|
<Tag>{schema.title}</Tag>
|
||||||
<span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>{item.name}</span>
|
<span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
extra={[
|
extra={[
|
||||||
@ -97,13 +262,11 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
console.log('info.key', info.key, schema);
|
console.log('info.key', info.key, schema);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{options.map(option => (
|
{options.map((option) => (
|
||||||
<Menu.ItemGroup title={option.label}>
|
<Menu.ItemGroup title={option.label}>
|
||||||
{
|
{option.children.map((item) => (
|
||||||
option.children.map(item => (
|
<Menu.Item key={item.name}>{item.title}</Menu.Item>
|
||||||
<Menu.Item key={item.name}>{item.title}</Menu.Item>
|
))}
|
||||||
))
|
|
||||||
}
|
|
||||||
</Menu.ItemGroup>
|
</Menu.ItemGroup>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
3
packages/client/src/schemas/database-field/style.less
Normal file
3
packages/client/src/schemas/database-field/style.less
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.ant-collapse.empty {
|
||||||
|
border: 0;
|
||||||
|
}
|
@ -21,7 +21,7 @@ import {
|
|||||||
import './style.less';
|
import './style.less';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
|
|
||||||
import { createSchema, useDesignable, useSchemaPath } from '../';
|
import { createSchema, removeSchema, useDesignable, useSchemaPath } from '../';
|
||||||
import {
|
import {
|
||||||
useDrag,
|
useDrag,
|
||||||
useDrop,
|
useDrop,
|
||||||
@ -37,6 +37,10 @@ export const GridBlockContext = createContext({
|
|||||||
dragRef: null,
|
dragRef: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function isGridRowOrCol(schema: ISchema) {
|
||||||
|
return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']);
|
||||||
|
}
|
||||||
|
|
||||||
const RowDivider = ({ name, onDrop }) => {
|
const RowDivider = ({ name, onDrop }) => {
|
||||||
const uid = useDragDropUID();
|
const uid = useDragDropUID();
|
||||||
const { isOver, dropRef } = useDrop({
|
const { isOver, dropRef } = useDrop({
|
||||||
@ -111,7 +115,11 @@ export const Grid: any = observer((props) => {
|
|||||||
});
|
});
|
||||||
await createSchema(data);
|
await createSchema(data);
|
||||||
console.log('prepend', 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) => {
|
{schema.mapProperties((property, key, index) => {
|
||||||
@ -143,8 +151,12 @@ export const Grid: any = observer((props) => {
|
|||||||
[...gridPath, key],
|
[...gridPath, key],
|
||||||
);
|
);
|
||||||
await createSchema(data);
|
await createSchema(data);
|
||||||
console.log('insertAfter', data);
|
const removed = deepRemove(path);
|
||||||
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);
|
await createSchema(data);
|
||||||
const path = [...e.dragItem.path];
|
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) => {
|
{schema.mapProperties((property, key, index) => {
|
||||||
@ -207,10 +223,14 @@ Grid.Row = observer((props) => {
|
|||||||
},
|
},
|
||||||
[...rowPath, key],
|
[...rowPath, key],
|
||||||
);
|
);
|
||||||
console.log('ColDivider', data)
|
console.log('ColDivider', data);
|
||||||
await createSchema(data);
|
await createSchema(data);
|
||||||
const path = [...e.dragItem.path];
|
const path = [...e.dragItem.path];
|
||||||
deepRemove(path);
|
const removed = deepRemove(path);
|
||||||
|
const last = removed.pop();
|
||||||
|
if (isGridRowOrCol(last)) {
|
||||||
|
await removeSchema(last);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onDragEnd={(e) => {
|
onDragEnd={(e) => {
|
||||||
designableSchema.mapProperties((s, key, index) => {
|
designableSchema.mapProperties((s, key, index) => {
|
||||||
|
@ -18,6 +18,23 @@ export const request = extend({
|
|||||||
timeout: 1000,
|
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) {
|
export async function createSchema(schema: ISchema) {
|
||||||
if (!schema['key']) {
|
if (!schema['key']) {
|
||||||
|
24
packages/plugin-collections/src/actions/index.ts
Normal file
24
packages/plugin-collections/src/actions/index.ts
Normal file
@ -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;
|
||||||
|
}
|
@ -2,6 +2,7 @@ import path from 'path';
|
|||||||
import { Application } from '@nocobase/server';
|
import { Application } from '@nocobase/server';
|
||||||
import { registerModels, Table } from '@nocobase/database';
|
import { registerModels, Table } from '@nocobase/database';
|
||||||
import * as models from './models';
|
import * as models from './models';
|
||||||
|
import { createOrUpdate } from './actions';
|
||||||
|
|
||||||
export default async function (this: Application, options = {}) {
|
export default async function (this: Application, options = {}) {
|
||||||
const database = this.database;
|
const database = this.database;
|
||||||
@ -16,4 +17,6 @@ export default async function (this: Application, options = {}) {
|
|||||||
model.set('name', model.get('key'));
|
model.set('name', model.get('key'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.resourcer.registerActionHandler('collections:createOrUpdate', createOrUpdate);
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,19 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
|
|||||||
payload: 'replace',
|
payload: 'replace',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await actions.common.create(ctx, async () => {});
|
await actions.common.create(ctx, async () => { });
|
||||||
|
const sticky = values['__prepend__'];
|
||||||
const targetKey = values['__insertAfter__'] || values['__insertBefore__'];
|
const targetKey = values['__insertAfter__'] || values['__insertBefore__'];
|
||||||
if (targetKey) {
|
if (sticky || targetKey) {
|
||||||
console.log({
|
console.log({
|
||||||
associatedKey: values.parentKey,
|
associatedKey: values.parentKey,
|
||||||
resourceKey: ctx.body.key,
|
resourceKey: ctx.body.key,
|
||||||
values: {
|
values: sticky ? {
|
||||||
field: 'sort',
|
field: 'sort',
|
||||||
|
sticky: true,
|
||||||
|
} : {
|
||||||
|
field: 'sort',
|
||||||
|
insertAfter: !!values['__insertAfter__'],
|
||||||
target: {
|
target: {
|
||||||
key: targetKey,
|
key: targetKey,
|
||||||
},
|
},
|
||||||
@ -35,7 +40,11 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
|
|||||||
{
|
{
|
||||||
associatedKey: values.parentKey,
|
associatedKey: values.parentKey,
|
||||||
resourceKey: ctx.body.key,
|
resourceKey: ctx.body.key,
|
||||||
values: {
|
values: sticky ? {
|
||||||
|
field: 'sort',
|
||||||
|
sticky: true,
|
||||||
|
target: {},
|
||||||
|
} : {
|
||||||
field: 'sort',
|
field: 'sort',
|
||||||
insertAfter: !!values['__insertAfter__'],
|
insertAfter: !!values['__insertAfter__'],
|
||||||
target: {
|
target: {
|
||||||
@ -47,8 +56,8 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
|
|||||||
payload: 'replace',
|
payload: 'replace',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await middlewares.associated(ctx, async () => {});
|
await middlewares.associated(ctx, async () => { });
|
||||||
await sort(ctx, async () => {});
|
await sort(ctx, async () => { });
|
||||||
}
|
}
|
||||||
await next();
|
await next();
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user