updates
This commit is contained in:
parent
e21a9ab57d
commit
3223d95d01
@ -1,5 +1,6 @@
|
|||||||
import { SchemaRenderer } from '../../';
|
import { SchemaRenderer } from '../../';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const schema = {
|
const schema = {
|
||||||
@ -32,5 +33,5 @@ export default () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return <SchemaRenderer schema={schema} />;
|
return <SchemaRenderer components={{ FormItem }} schema={schema} />;
|
||||||
};
|
};
|
||||||
|
@ -69,10 +69,11 @@ export function createRouteSwitch(options: RouteSwitchOptions) {
|
|||||||
value={{ ...options.components, ...props.components }}
|
value={{ ...options.components, ...props.components }}
|
||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map((route) => {
|
{routes.map((route, index) => {
|
||||||
if (route.type == 'redirect') {
|
if (route.type == 'redirect') {
|
||||||
return (
|
return (
|
||||||
<Redirect
|
<Redirect
|
||||||
|
key={index}
|
||||||
to={route.to}
|
to={route.to}
|
||||||
push={route.push}
|
push={route.push}
|
||||||
from={route.from}
|
from={route.from}
|
||||||
@ -87,6 +88,7 @@ export function createRouteSwitch(options: RouteSwitchOptions) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Route
|
<Route
|
||||||
|
key={index}
|
||||||
path={route.path}
|
path={route.path}
|
||||||
exact={route.exact}
|
exact={route.exact}
|
||||||
strict={route.strict}
|
strict={route.strict}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import request from 'umi-request';
|
import { request } from './schemas';
|
||||||
|
|
||||||
export interface ResourceOptions {
|
export interface ResourceOptions {
|
||||||
resourceName: string;
|
resourceName: string;
|
||||||
@ -33,7 +33,7 @@ export class Resource {
|
|||||||
|
|
||||||
list(options: ListOptions = {}) {
|
list(options: ListOptions = {}) {
|
||||||
const { resourceName } = this.options;
|
const { resourceName } = this.options;
|
||||||
return request(`/api/${resourceName}:list`);
|
return request(`${resourceName}:list`);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(options: GetOptions = {}) {
|
get(options: GetOptions = {}) {
|
||||||
@ -42,19 +42,30 @@ export class Resource {
|
|||||||
if (!resourceKey) {
|
if (!resourceKey) {
|
||||||
return Promise.resolve({ data: {} });
|
return Promise.resolve({ data: {} });
|
||||||
}
|
}
|
||||||
return request(`/api/${resourceName}:get/${resourceKey}`);
|
return request(`${resourceName}:get/${resourceKey}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
save(values: any, options: SaveOptions = {}) {
|
save(values: any, options: SaveOptions = {}) {
|
||||||
const resourceKey = options.resourceKey || this.options.resourceKey;
|
const resourceKey = options.resourceKey || this.options.resourceKey;
|
||||||
const { resourceName } = this.options;
|
const { resourceName } = this.options;
|
||||||
const url = `/api/${resourceName}:${resourceKey ? `update/${resourceKey}` : 'create'}`;
|
const url = `${resourceName}:${resourceKey ? `update/${resourceKey}` : 'create'}`;
|
||||||
return request(url, {
|
return request(url, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: values,
|
data: values,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy(filter: any) {
|
||||||
|
const { resourceName } = this.options;
|
||||||
|
const url = `${resourceName}:destroy`;
|
||||||
|
return request(url, {
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
filter
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static make(options: string | Resource | ResourceOptions) {
|
static make(options: string | Resource | ResourceOptions) {
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string') {
|
||||||
return new Resource({ resourceName: options });
|
return new Resource({ resourceName: options });
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
|
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
|
||||||
import { Link, useHistory, LinkProps } from 'react-router-dom';
|
import { Link, useHistory, LinkProps } from 'react-router-dom';
|
||||||
import { useDesignable, VisibleContext, useDefaultAction } from '..';
|
import { useDesignable, VisibleContext, useDefaultAction, useVisible } from '..';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
@ -25,8 +25,9 @@ import { useSchemaComponent } from '../../components/schema-renderer';
|
|||||||
export const Action: any = observer((props: any) => {
|
export const Action: any = observer((props: any) => {
|
||||||
const { useAction = useDefaultAction, icon, ...others } = props;
|
const { useAction = useDefaultAction, icon, ...others } = props;
|
||||||
const { run } = useAction();
|
const { run } = useAction();
|
||||||
|
const field = useField();
|
||||||
const { schema, DesignableBar } = useDesignable();
|
const { schema, DesignableBar } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const { visible, setVisible } = useVisible(field.address.entire);
|
||||||
const child = Object.values(schema.properties || {}).shift();
|
const child = Object.values(schema.properties || {}).shift();
|
||||||
const isDropdownOrPopover =
|
const isDropdownOrPopover =
|
||||||
child &&
|
child &&
|
||||||
@ -74,6 +75,7 @@ Action.Modal = observer((props: any) => {
|
|||||||
} = props;
|
} = props;
|
||||||
const { schema } = useDesignable();
|
const { schema } = useDesignable();
|
||||||
const [visible, setVisible] = useContext(VisibleContext);
|
const [visible, setVisible] = useContext(VisibleContext);
|
||||||
|
console.log('VisibleContext', visible);
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const { run: runOk } = useOkAction();
|
const { run: runOk } = useOkAction();
|
||||||
const { run: runCancel } = useCancelAction();
|
const { run: runCancel } = useCancelAction();
|
||||||
@ -81,7 +83,7 @@ Action.Modal = observer((props: any) => {
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={schema.title}
|
title={schema.title}
|
||||||
destroyOnClose
|
// destroyOnClose
|
||||||
maskClosable
|
maskClosable
|
||||||
footer={
|
footer={
|
||||||
isFormDecorator
|
isFormDecorator
|
||||||
|
@ -64,6 +64,8 @@ import { cloneDeep } from 'lodash';
|
|||||||
import { options } from '../database-field/interfaces';
|
import { options } from '../database-field/interfaces';
|
||||||
import { useDisplayFieldsContext } from '../form';
|
import { useDisplayFieldsContext } from '../form';
|
||||||
|
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
const generateGridBlock = (schema: ISchema) => {
|
const generateGridBlock = (schema: ISchema) => {
|
||||||
const name = schema.name || uid();
|
const name = schema.name || uid();
|
||||||
return {
|
return {
|
||||||
@ -117,17 +119,17 @@ function generateCardItemSchema(component) {
|
|||||||
'x-decorator': 'CardItem',
|
'x-decorator': 'CardItem',
|
||||||
'x-component': 'Table',
|
'x-component': 'Table',
|
||||||
default: [
|
default: [
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
{ key: uid(), field1: uid(), field2: uid() },
|
// { key: uid(), field1: uid(), field2: uid() },
|
||||||
],
|
],
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
rowKey: 'key',
|
rowKey: 'id',
|
||||||
dragSort: true,
|
dragSort: true,
|
||||||
showIndex: true,
|
showIndex: true,
|
||||||
},
|
},
|
||||||
@ -232,45 +234,34 @@ function generateCardItemSchema(component) {
|
|||||||
'x-component': 'Menu.Action',
|
'x-component': 'Menu.Action',
|
||||||
'x-designable-bar': 'Table.Action.DesignableBar',
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '查看',
|
title: '查看',
|
||||||
'x-component': 'Action.Modal',
|
'x-component': 'Action.Modal',
|
||||||
'x-component-props': {},
|
'x-component-props': {
|
||||||
|
bodyStyle: {
|
||||||
|
background: '#f0f2f5',
|
||||||
|
paddingTop: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Tabs',
|
'x-component': 'Tabs',
|
||||||
properties: {
|
properties: {
|
||||||
tab1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Tabs.TabPane',
|
'x-component': 'Tabs.TabPane',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
tab: 'Tab1',
|
tab: 'Tab1',
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
aaa: {
|
[uid()]: {
|
||||||
type: 'string',
|
type: 'void',
|
||||||
title: 'AAA',
|
'x-component': 'Grid',
|
||||||
'x-decorator': 'FormItem',
|
'x-component-props': {
|
||||||
required: true,
|
addNewComponent: 'AddNew.PaneItem',
|
||||||
'x-component': 'Input',
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tab2: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Tabs.TabPane',
|
|
||||||
'x-component-props': {
|
|
||||||
tab: 'Tab2',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
bbb: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'BBB',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
required: true,
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -282,14 +273,13 @@ function generateCardItemSchema(component) {
|
|||||||
},
|
},
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'action1',
|
title: '编辑',
|
||||||
title: '修改',
|
|
||||||
'x-component': 'Menu.Action',
|
'x-component': 'Menu.Action',
|
||||||
'x-designable-bar': 'Table.Action.DesignableBar',
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '编辑表单',
|
title: '编辑数据',
|
||||||
'x-decorator': 'Form',
|
'x-decorator': 'Form',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
useValues: '{{ Table.useTableRow }}',
|
useValues: '{{ Table.useTableRow }}',
|
||||||
@ -299,17 +289,12 @@ function generateCardItemSchema(component) {
|
|||||||
useOkAction: '{{ Table.useTableUpdateAction }}',
|
useOkAction: '{{ Table.useTableUpdateAction }}',
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
field1: {
|
[uid()]: {
|
||||||
type: 'string',
|
type: 'void',
|
||||||
title: '字段1',
|
'x-component': 'Grid',
|
||||||
'x-decorator': 'FormItem',
|
'x-component-props': {
|
||||||
'x-component': 'Input',
|
addNewComponent: 'AddNew.FormItem',
|
||||||
},
|
},
|
||||||
field2: {
|
|
||||||
type: 'string',
|
|
||||||
title: '字段2',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -327,48 +312,6 @@ function generateCardItemSchema(component) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
title: '字段1',
|
|
||||||
'x-component': 'Table.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
// title: 'z1',
|
|
||||||
},
|
|
||||||
'x-designable-bar': 'Table.Column.DesignableBar',
|
|
||||||
properties: {
|
|
||||||
field1: {
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
'x-read-pretty': true,
|
|
||||||
'x-decorator-props': {
|
|
||||||
feedbackLayout: 'popover',
|
|
||||||
},
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
title: '字段2',
|
|
||||||
'x-component': 'Table.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
// title: 'z1',
|
|
||||||
},
|
|
||||||
'x-designable-bar': 'Table.Column.DesignableBar',
|
|
||||||
properties: {
|
|
||||||
field2: {
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
'x-read-pretty': true,
|
|
||||||
'x-decorator-props': {
|
|
||||||
feedbackLayout: 'popover',
|
|
||||||
},
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Form: {
|
Form: {
|
||||||
@ -679,7 +622,7 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
placement={'bottomCenter'}
|
placement={'bottomCenter'}
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.ItemGroup title={`要展示的字段`}>
|
<Menu.ItemGroup className={'display-fields'} title={`要展示的字段`}>
|
||||||
{resource?.fields?.map((field) => (
|
{resource?.fields?.map((field) => (
|
||||||
<Menu.Item key={field.key}>
|
<Menu.Item key={field.key}>
|
||||||
<FieldSwitch
|
<FieldSwitch
|
||||||
@ -723,7 +666,11 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
))}
|
))}
|
||||||
</Menu.ItemGroup>
|
</Menu.ItemGroup>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.SubMenu title={'新建字段'}>
|
<Menu.SubMenu
|
||||||
|
popupClassName={'add-new-fields-popup'}
|
||||||
|
className={'sub-menu-add-new-fields'}
|
||||||
|
title={'新建字段'}
|
||||||
|
>
|
||||||
{options.map(
|
{options.map(
|
||||||
(option) =>
|
(option) =>
|
||||||
option.children.length > 0 && (
|
option.children.length > 0 && (
|
||||||
@ -827,4 +774,163 @@ AddNew.FormItem = observer((props: any) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddNew.PaneItem = observer((props: any) => {
|
||||||
|
const { ghost, defaultAction } = props;
|
||||||
|
const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
|
||||||
|
const path = useSchemaPath();
|
||||||
|
const { resource = {}, refresh } = useResourceContext();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { displayFields = [] } = useDisplayFieldsContext();
|
||||||
|
console.log({ displayFields });
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={setVisible}
|
||||||
|
overlayStyle={{
|
||||||
|
minWidth: 200,
|
||||||
|
}}
|
||||||
|
placement={'bottomCenter'}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.SubMenu title={'新建卡片'}>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={async () => {
|
||||||
|
let data: ISchema = {
|
||||||
|
type: 'void',
|
||||||
|
name: uid(),
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Form',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-designable-bar': 'Form.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-component-props': {
|
||||||
|
addNewComponent: 'AddNew.FormItem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (isGridBlock(schema)) {
|
||||||
|
path.pop();
|
||||||
|
path.pop();
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
} else if (isGrid(schema)) {
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
let s;
|
||||||
|
if (isGrid(schema)) {
|
||||||
|
s = appendChild(data, [...path]);
|
||||||
|
} else if (defaultAction === 'insertAfter') {
|
||||||
|
s = insertAfter(data, [...path]);
|
||||||
|
} else {
|
||||||
|
s = insertBefore(data, [...path]);
|
||||||
|
}
|
||||||
|
await createSchema(s);
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
style={{ minWidth: 150 }}
|
||||||
|
>
|
||||||
|
阅读模式
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={async () => {
|
||||||
|
let data: ISchema = {
|
||||||
|
type: 'void',
|
||||||
|
name: uid(),
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Form',
|
||||||
|
'x-component-props': {
|
||||||
|
showDefaultButtons: true,
|
||||||
|
},
|
||||||
|
'x-designable-bar': 'Form.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-component-props': {
|
||||||
|
addNewComponent: 'AddNew.FormItem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (isGridBlock(schema)) {
|
||||||
|
path.pop();
|
||||||
|
path.pop();
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
} else if (isGrid(schema)) {
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
let s;
|
||||||
|
if (isGrid(schema)) {
|
||||||
|
s = appendChild(data, [...path]);
|
||||||
|
} else if (defaultAction === 'insertAfter') {
|
||||||
|
s = insertAfter(data, [...path]);
|
||||||
|
} else {
|
||||||
|
s = insertBefore(data, [...path]);
|
||||||
|
}
|
||||||
|
await createSchema(s);
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑模式
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.SubMenu>
|
||||||
|
<Menu.SubMenu title={'要展示的相关数据'}>
|
||||||
|
<Menu.Item style={{ minWidth: 150 }}>日志</Menu.Item>
|
||||||
|
<Menu.Item>评论</Menu.Item>
|
||||||
|
</Menu.SubMenu>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={async () => {
|
||||||
|
let data: ISchema = {
|
||||||
|
key: uid(),
|
||||||
|
type: 'void',
|
||||||
|
default: '这是一段演示文字,**支持使用 Markdown 语法**',
|
||||||
|
'x-designable-bar': 'Markdown.Void.DesignableBar',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-component': 'Markdown.Void',
|
||||||
|
};
|
||||||
|
if (isGridBlock(schema)) {
|
||||||
|
path.pop();
|
||||||
|
path.pop();
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
} else if (isGrid(schema)) {
|
||||||
|
data = generateGridBlock(data);
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
let s;
|
||||||
|
if (isGrid(schema)) {
|
||||||
|
s = appendChild(data, [...path]);
|
||||||
|
} else if (defaultAction === 'insertAfter') {
|
||||||
|
s = insertAfter(data, [...path]);
|
||||||
|
} else {
|
||||||
|
s = insertBefore(data, [...path]);
|
||||||
|
}
|
||||||
|
await createSchema(s);
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
添加说明文字
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ghost ? (
|
||||||
|
<PlusOutlined />
|
||||||
|
) : (
|
||||||
|
<Button block type={'dashed'} icon={<PlusOutlined />}></Button>
|
||||||
|
)}
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default AddNew;
|
export default AddNew;
|
||||||
|
10
packages/client/src/schemas/add-new/style.less
Normal file
10
packages/client/src/schemas/add-new/style.less
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.ant-dropdown-menu-item-group.display-fields {
|
||||||
|
.ant-dropdown-menu-item-group-list {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.add-new-fields-popup .ant-dropdown-menu {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
@ -145,6 +145,7 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
{field.value?.map((item, index) => {
|
{field.value?.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<Select.Option
|
<Select.Option
|
||||||
|
key={index}
|
||||||
value={index}
|
value={index}
|
||||||
label={`${item.title || '未命名'}${
|
label={`${item.title || '未命名'}${
|
||||||
item.unsaved ? ' (未保存)' : ''
|
item.unsaved ? ' (未保存)' : ''
|
||||||
@ -254,6 +255,7 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
address: `*(${path},${path}.*)`,
|
address: `*(${path},${path}.*)`,
|
||||||
});
|
});
|
||||||
|
console.log('item.key', item.key);
|
||||||
return (
|
return (
|
||||||
<Collapse.Panel
|
<Collapse.Panel
|
||||||
header={
|
header={
|
||||||
@ -268,8 +270,9 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
extra={[
|
extra={[
|
||||||
<Badge count={errors.length} />,
|
<Badge key={'1'} count={errors.length} />,
|
||||||
<DeleteOutlined
|
<DeleteOutlined
|
||||||
|
key={'2'}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
field.remove(index);
|
field.remove(index);
|
||||||
@ -305,6 +308,7 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
</Collapse>
|
</Collapse>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placement={'bottomCenter'}
|
placement={'bottomCenter'}
|
||||||
|
overlayClassName={'all-fields'}
|
||||||
overlayStyle={{
|
overlayStyle={{
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
}}
|
}}
|
||||||
@ -327,9 +331,9 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{options.map(
|
{options.map(
|
||||||
(option) =>
|
(option, groupIndex) =>
|
||||||
option.children.length > 0 && (
|
option.children.length > 0 && (
|
||||||
<Menu.ItemGroup title={option.label}>
|
<Menu.ItemGroup key={groupIndex} 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>
|
||||||
))}
|
))}
|
||||||
|
@ -14,3 +14,10 @@
|
|||||||
background: #1890ff;
|
background: #1890ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-dropdown.all-fields {
|
||||||
|
.ant-dropdown-menu {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ import {
|
|||||||
ResourceContextProvider,
|
ResourceContextProvider,
|
||||||
} from '../';
|
} from '../';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { Button, Dropdown, Menu, Space } from 'antd';
|
import { Button, Dropdown, Menu, message, Space } from 'antd';
|
||||||
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { FormLayout } from '@formily/antd';
|
import { FormLayout } from '@formily/antd';
|
||||||
@ -38,6 +38,7 @@ import { DesignableBar } from './DesignableBar';
|
|||||||
import { FieldDesignableBar } from './Field.DesignableBar';
|
import { FieldDesignableBar } from './Field.DesignableBar';
|
||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { BlockItem } from '../block-item';
|
import { BlockItem } from '../block-item';
|
||||||
|
import { Resource } from '../../resource';
|
||||||
|
|
||||||
const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => {
|
const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => {
|
||||||
const [displayFields, setDisplayFields] = useState([]);
|
const [displayFields, setDisplayFields] = useState([]);
|
||||||
@ -46,12 +47,12 @@ const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => {
|
|||||||
displayFields,
|
displayFields,
|
||||||
setDisplayFields,
|
setDisplayFields,
|
||||||
addDisplayField(fieldName) {
|
addDisplayField(fieldName) {
|
||||||
setDisplayFields(fields => fields.concat(fieldName));
|
setDisplayFields((fields) => fields.concat(fieldName));
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export { DisplayFieldsContextProvider, useDisplayFieldsContext }
|
export { DisplayFieldsContextProvider, useDisplayFieldsContext };
|
||||||
|
|
||||||
export const Form: any = observer((props: any) => {
|
export const Form: any = observer((props: any) => {
|
||||||
const {
|
const {
|
||||||
@ -60,12 +61,12 @@ export const Form: any = observer((props: any) => {
|
|||||||
resourceName,
|
resourceName,
|
||||||
...others
|
...others
|
||||||
} = props;
|
} = props;
|
||||||
|
const { schema } = useDesignable();
|
||||||
const initialValues = useValues();
|
const initialValues = useValues();
|
||||||
const form = useMemo(() => {
|
const form = useMemo(() => {
|
||||||
console.log('Form.useMemo', initialValues);
|
console.log('Form.useMemo', initialValues);
|
||||||
return createForm({ initialValues });
|
return createForm({ initialValues, readPretty: schema['x-read-pretty'] });
|
||||||
}, []);
|
}, []);
|
||||||
const { schema } = useDesignable();
|
|
||||||
const path = useSchemaPath();
|
const path = useSchemaPath();
|
||||||
const scope = useContext(SchemaExpressionScopeContext);
|
const scope = useContext(SchemaExpressionScopeContext);
|
||||||
const content = (
|
const content = (
|
||||||
@ -102,6 +103,9 @@ export const Form: any = observer((props: any) => {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const values = await form.submit();
|
const values = await form.submit();
|
||||||
console.log({ values });
|
console.log({ values });
|
||||||
|
await Resource.make(resourceName).save(values);
|
||||||
|
message.success('保存成功');
|
||||||
|
await form.reset();
|
||||||
}}
|
}}
|
||||||
type={'primary'}
|
type={'primary'}
|
||||||
>
|
>
|
||||||
@ -121,9 +125,7 @@ export const Form: any = observer((props: any) => {
|
|||||||
return resourceName ? (
|
return resourceName ? (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<ResourceContextProvider resourceName={resourceName}>
|
<ResourceContextProvider resourceName={resourceName}>
|
||||||
<DisplayFieldsContextProvider>
|
<DisplayFieldsContextProvider>{content}</DisplayFieldsContextProvider>
|
||||||
{content}
|
|
||||||
</DisplayFieldsContextProvider>
|
|
||||||
</ResourceContextProvider>
|
</ResourceContextProvider>
|
||||||
) : (
|
) : (
|
||||||
content
|
content
|
||||||
@ -141,7 +143,7 @@ Form.Field = observer((props: any) => {
|
|||||||
const { resource = {} } = useResourceContext();
|
const { resource = {} } = useResourceContext();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (fieldName) {
|
if (fieldName) {
|
||||||
addDisplayField(fieldName);
|
addDisplayField && addDisplayField(fieldName);
|
||||||
}
|
}
|
||||||
}, [fieldName]);
|
}, [fieldName]);
|
||||||
if (!fieldName) {
|
if (!fieldName) {
|
||||||
|
@ -32,6 +32,21 @@ export function mapReadPretty(component: any, readPrettyProps?: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const VisibleContext = createContext(null);
|
export const VisibleContext = createContext(null);
|
||||||
|
|
||||||
|
export function useVisible(name, defaultValue = false) {
|
||||||
|
const [visible, setVisible] = useState(defaultValue);
|
||||||
|
return { visible, setVisible };
|
||||||
|
// const [visible, setVisible] = useCookieState(`${name}-visible`, {
|
||||||
|
// defaultValue: defaultValue ? 'true' : null,
|
||||||
|
// });
|
||||||
|
// return {
|
||||||
|
// visible: visible === 'true' ? true : false,
|
||||||
|
// setVisible: (value) => {
|
||||||
|
// setVisible(value ? 'true' : null);
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
export const DesignableBarContext = createContext(null);
|
export const DesignableBarContext = createContext(null);
|
||||||
|
|
||||||
const [PageTitleContextProvider, usePageTitleContext] = constate(() => {
|
const [PageTitleContextProvider, usePageTitleContext] = constate(() => {
|
||||||
|
@ -24,7 +24,6 @@ type ComposedInput = React.FC<InputProps> & {
|
|||||||
export const Input: ComposedInput = connect(
|
export const Input: ComposedInput = connect(
|
||||||
AntdInput,
|
AntdInput,
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
console.log('fieldfieldfieldfieldfield', field.address.entire);
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
suffix: (
|
suffix: (
|
||||||
|
@ -55,6 +55,7 @@ import {
|
|||||||
removeSchema,
|
removeSchema,
|
||||||
updateSchema,
|
updateSchema,
|
||||||
useDefaultAction,
|
useDefaultAction,
|
||||||
|
useVisible,
|
||||||
VisibleContext,
|
VisibleContext,
|
||||||
} from '..';
|
} from '..';
|
||||||
import { useMount } from 'ahooks';
|
import { useMount } from 'ahooks';
|
||||||
@ -205,7 +206,8 @@ Menu.Link = observer((props: any) => {
|
|||||||
eventKey={schema.name}
|
eventKey={schema.name}
|
||||||
key={schema.name}
|
key={schema.name}
|
||||||
>
|
>
|
||||||
<Link to={props.to}>{schema.title}</Link>
|
{schema.title}
|
||||||
|
{/* <Link to={props.to || schema.name}>{schema.title}</Link> */}
|
||||||
<DesignableBar />
|
<DesignableBar />
|
||||||
</AntdMenu.Item>
|
</AntdMenu.Item>
|
||||||
);
|
);
|
||||||
@ -232,7 +234,8 @@ Menu.URL = observer((props: any) => {
|
|||||||
Menu.Action = observer((props: any) => {
|
Menu.Action = observer((props: any) => {
|
||||||
const { icon, useAction = useDefaultAction, ...others } = props;
|
const { icon, useAction = useDefaultAction, ...others } = props;
|
||||||
const { schema, DesignableBar } = useDesignable();
|
const { schema, DesignableBar } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const field = useField();
|
||||||
|
const { visible, setVisible } = useVisible(field.address.entire);
|
||||||
const { run } = useAction();
|
const { run } = useAction();
|
||||||
return (
|
return (
|
||||||
<VisibleContext.Provider value={[visible, setVisible]}>
|
<VisibleContext.Provider value={[visible, setVisible]}>
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
import React, { createContext, useContext, useRef, useState } from 'react';
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
useFieldSchema,
|
useFieldSchema,
|
||||||
Schema,
|
Schema,
|
||||||
@ -22,7 +28,7 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
Switch,
|
Switch,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { findIndex, get, set } from 'lodash';
|
import { cloneDeep, findIndex, get, set } from 'lodash';
|
||||||
import constate from 'constate';
|
import constate from 'constate';
|
||||||
import useRequest from '@ahooksjs/use-request';
|
import useRequest from '@ahooksjs/use-request';
|
||||||
import { uid, clone } from '@formily/shared';
|
import { uid, clone } from '@formily/shared';
|
||||||
@ -39,9 +45,11 @@ import {
|
|||||||
} from 'react-sortable-hoc';
|
} from 'react-sortable-hoc';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import {
|
import {
|
||||||
|
createSchema,
|
||||||
getSchemaPath,
|
getSchemaPath,
|
||||||
removeSchema,
|
removeSchema,
|
||||||
ResourceContextProvider,
|
ResourceContextProvider,
|
||||||
|
updateSchema,
|
||||||
useCollectionContext,
|
useCollectionContext,
|
||||||
useDesignable,
|
useDesignable,
|
||||||
useResourceContext,
|
useResourceContext,
|
||||||
@ -53,6 +61,8 @@ import { DraggableBlockContext } from '../../components/drag-and-drop';
|
|||||||
import AddNew from '../add-new';
|
import AddNew from '../add-new';
|
||||||
import { isGridRowOrCol } from '../grid';
|
import { isGridRowOrCol } from '../grid';
|
||||||
import { options } from '../database-field/interfaces';
|
import { options } from '../database-field/interfaces';
|
||||||
|
import { Resource } from '../../resource';
|
||||||
|
import { useDisplayFieldsContext } from '../form';
|
||||||
|
|
||||||
interface TableRowProps {
|
interface TableRowProps {
|
||||||
index: number;
|
index: number;
|
||||||
@ -131,10 +141,13 @@ function useTableActionBars() {
|
|||||||
return findActionBars(schema);
|
return findActionBars(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ResourceFieldContext = createContext(null);
|
||||||
|
|
||||||
function useTableColumns(props?: any) {
|
function useTableColumns(props?: any) {
|
||||||
const { schema, designable } = useDesignable();
|
const { schema, designable } = useDesignable();
|
||||||
// const schema = useFieldSchema();
|
// const schema = useFieldSchema();
|
||||||
const { dataSource } = props || {};
|
const { dataSource, rowKey = 'id' } = props || {};
|
||||||
|
const { resource = {} } = useResourceContext();
|
||||||
|
|
||||||
function findColumns(schema: Schema): Schema[] {
|
function findColumns(schema: Schema): Schema[] {
|
||||||
return schema.reduceProperties((columns, current) => {
|
return schema.reduceProperties((columns, current) => {
|
||||||
@ -148,21 +161,35 @@ function useTableColumns(props?: any) {
|
|||||||
const columns = findColumns(schema).map((item) => {
|
const columns = findColumns(schema).map((item) => {
|
||||||
const columnProps = item['x-component-props'] || {};
|
const columnProps = item['x-component-props'] || {};
|
||||||
console.log(item);
|
console.log(item);
|
||||||
|
const resourceField = columnProps.fieldName
|
||||||
|
? resource?.fields?.find((item) => item.name === columnProps.fieldName)
|
||||||
|
: null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: <RecursionField name={item.name} schema={item} onlyRenderSelf />,
|
title: (
|
||||||
|
<ResourceFieldContext.Provider value={resourceField}>
|
||||||
|
<RecursionField name={item.name} schema={item} onlyRenderSelf />
|
||||||
|
</ResourceFieldContext.Provider>
|
||||||
|
),
|
||||||
dataIndex: item.name,
|
dataIndex: item.name,
|
||||||
...columnProps,
|
...columnProps,
|
||||||
render(value, record, recordIndex) {
|
render(value, record, recordIndex) {
|
||||||
const index = dataSource.indexOf(record);
|
const index = findIndex(
|
||||||
|
dataSource,
|
||||||
|
(item) => item[rowKey] === record[rowKey],
|
||||||
|
);
|
||||||
|
console.log({ dataSource, record, index });
|
||||||
return (
|
return (
|
||||||
<TableRowContext.Provider
|
<ResourceFieldContext.Provider value={resourceField}>
|
||||||
value={{
|
<TableRowContext.Provider
|
||||||
index: index,
|
value={{
|
||||||
data: record,
|
index: index,
|
||||||
}}
|
data: record,
|
||||||
>
|
}}
|
||||||
<RecursionField schema={item} name={index} onlyRenderProperties />
|
>
|
||||||
</TableRowContext.Provider>
|
<Table.Cell schema={item} />
|
||||||
|
</TableRowContext.Provider>
|
||||||
|
</ResourceFieldContext.Provider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -198,6 +225,7 @@ function useTableColumns(props?: any) {
|
|||||||
|
|
||||||
function AddColumn() {
|
function AddColumn() {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { appendChild } = useDesignable();
|
||||||
const { resource = {}, refresh } = useResourceContext();
|
const { resource = {}, refresh } = useResourceContext();
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@ -206,7 +234,7 @@ function AddColumn() {
|
|||||||
onVisibleChange={setVisible}
|
onVisibleChange={setVisible}
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.ItemGroup title={'要展示的字段'}>
|
<Menu.ItemGroup className={'display-fields'} title={'要展示的字段'}>
|
||||||
{resource?.fields?.map((field) => (
|
{resource?.fields?.map((field) => (
|
||||||
<Menu.Item style={{ minWidth: 150 }}>
|
<Menu.Item style={{ minWidth: 150 }}>
|
||||||
<div
|
<div
|
||||||
@ -215,15 +243,29 @@ function AddColumn() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
|
onClick={async () => {
|
||||||
|
const data = appendChild({
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
fieldName: field.name,
|
||||||
|
},
|
||||||
|
'x-designable-bar': 'Table.Column.DesignableBar',
|
||||||
|
});
|
||||||
|
await createSchema(data);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span>{field.uiSchema.title}</span>
|
<span>{field.uiSchema.title}</span>
|
||||||
<Switch size={'small'} defaultChecked />
|
<Switch size={'small'} />
|
||||||
</div>
|
</div>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu.ItemGroup>
|
</Menu.ItemGroup>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.SubMenu title={'新增字段'}>
|
<Menu.SubMenu
|
||||||
|
popupClassName={'add-new-fields-popup'}
|
||||||
|
title={'新增字段'}
|
||||||
|
>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<Menu.ItemGroup title={option.label}>
|
<Menu.ItemGroup title={option.label}>
|
||||||
{option.children.map((item) => (
|
{option.children.map((item) => (
|
||||||
@ -266,26 +308,31 @@ export function useTableIndex() {
|
|||||||
|
|
||||||
export function useTableDestroyAction() {
|
export function useTableDestroyAction() {
|
||||||
const ctx = useContext(TableRowContext);
|
const ctx = useContext(TableRowContext);
|
||||||
const { field, selectedRowKeys, setSelectedRowKeys, refresh } =
|
const { resource, field, selectedRowKeys, setSelectedRowKeys, refresh } =
|
||||||
useTableContext();
|
useTableContext();
|
||||||
const rowKey = field.componentProps.rowKey || 'id';
|
const rowKey = field.componentProps.rowKey || 'id';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
if (ctx && typeof ctx.index !== 'undefined') {
|
if (ctx && typeof ctx.index !== 'undefined') {
|
||||||
field.remove(ctx.index);
|
// field.remove(ctx.index);
|
||||||
refresh();
|
await resource.destroy({
|
||||||
|
[rowKey]: ctx.data[rowKey],
|
||||||
|
});
|
||||||
|
await refresh();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedRowKeys.length) {
|
if (!selectedRowKeys.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (selectedRowKeys.length) {
|
await resource.destroy({
|
||||||
const key = selectedRowKeys.shift();
|
[`${rowKey}.in`]: selectedRowKeys,
|
||||||
const index = findIndex(field.value, (item) => item[rowKey] === key);
|
});
|
||||||
field.remove(index);
|
// while (selectedRowKeys.length) {
|
||||||
}
|
// const key = selectedRowKeys.shift();
|
||||||
|
// const index = findIndex(field.value, (item) => item[rowKey] === key);
|
||||||
|
// field.remove(index);
|
||||||
|
// }
|
||||||
refresh();
|
refresh();
|
||||||
setSelectedRowKeys([]);
|
setSelectedRowKeys([]);
|
||||||
},
|
},
|
||||||
@ -305,16 +352,18 @@ export function useTableFilterAction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useTableCreateAction() {
|
export function useTableCreateAction() {
|
||||||
const { field, run: exec, params } = useTableContext();
|
const { resource, field, run: exec, params, refresh } = useTableContext();
|
||||||
const [, setVisible] = useContext(VisibleContext);
|
const [, setVisible] = useContext(VisibleContext);
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
setVisible && setVisible(false);
|
setVisible && setVisible(false);
|
||||||
field.push({
|
await resource.save(form.values);
|
||||||
key: uid(),
|
// await refresh();
|
||||||
...clone(form.values),
|
// field.push({
|
||||||
});
|
// key: uid(),
|
||||||
|
// ...clone(form.values),
|
||||||
|
// });
|
||||||
form.reset();
|
form.reset();
|
||||||
exec({ ...params, page: 1 });
|
exec({ ...params, page: 1 });
|
||||||
},
|
},
|
||||||
@ -322,14 +371,16 @@ export function useTableCreateAction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useTableUpdateAction() {
|
export function useTableUpdateAction() {
|
||||||
const { field, refresh, params } = useTableContext();
|
const { resource, field, refresh, params } = useTableContext();
|
||||||
const [, setVisible] = useContext(VisibleContext);
|
const [, setVisible] = useContext(VisibleContext);
|
||||||
const ctx = useContext(TableRowContext);
|
const ctx = useContext(TableRowContext);
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
field.value[ctx.index] = form.values;
|
|
||||||
setVisible && setVisible(false);
|
setVisible && setVisible(false);
|
||||||
|
await resource.save(form.values, {
|
||||||
|
resourceKey: form.values[field.componentProps.rowKey || 'id'],
|
||||||
|
});
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -342,6 +393,7 @@ const [TableContextProvider, useTableContext] = constate(() => {
|
|||||||
const [selectedRowKeys, setSelectedRowKeys] = useState(
|
const [selectedRowKeys, setSelectedRowKeys] = useState(
|
||||||
defaultSelectedRowKeys || [],
|
defaultSelectedRowKeys || [],
|
||||||
);
|
);
|
||||||
|
const resource = Resource.make(field.componentProps.resourceName);
|
||||||
console.log({ defaultSelectedRowKeys });
|
console.log({ defaultSelectedRowKeys });
|
||||||
const pagination = usePaginationProps();
|
const pagination = usePaginationProps();
|
||||||
const response = useRequest<{
|
const response = useRequest<{
|
||||||
@ -349,6 +401,21 @@ const [TableContextProvider, useTableContext] = constate(() => {
|
|||||||
total: number;
|
total: number;
|
||||||
}>((params = {}) => {
|
}>((params = {}) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
resource.list({}).then((res) => {
|
||||||
|
field.setValue(res?.data || []);
|
||||||
|
resolve({
|
||||||
|
list: res?.data || [],
|
||||||
|
total: res?.meta?.count || res?.data?.length,
|
||||||
|
});
|
||||||
|
console.log(
|
||||||
|
'field.componentProps.resourceName',
|
||||||
|
field.componentProps.resourceName,
|
||||||
|
res,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
|
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
|
||||||
if (pagination) {
|
if (pagination) {
|
||||||
const {
|
const {
|
||||||
@ -375,6 +442,7 @@ const [TableContextProvider, useTableContext] = constate(() => {
|
|||||||
...(response.params[0] || {}),
|
...(response.params[0] || {}),
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
resource,
|
||||||
...response,
|
...response,
|
||||||
params,
|
params,
|
||||||
field,
|
field,
|
||||||
@ -440,7 +508,10 @@ const TableContainer = observer((props) => {
|
|||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
renderCell: (checked, record, _, originNode) => {
|
renderCell: (checked, record, _, originNode) => {
|
||||||
const index = dataSource.indexOf(record);
|
const index = findIndex(
|
||||||
|
dataSource,
|
||||||
|
(item) => item[rowKey] === record[rowKey],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<TableRowContext.Provider
|
<TableRowContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@ -571,13 +642,79 @@ function useDesignableBar() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Table.Column = observer((props) => {
|
Table.Cell = observer((props: any) => {
|
||||||
const schema = useFieldSchema();
|
const ctx = useContext(TableRowContext);
|
||||||
const field = useField();
|
const schema = props.schema;
|
||||||
const { DesignableBar } = useDesignableBar();
|
const resourceField = useContext(ResourceFieldContext);
|
||||||
|
return (
|
||||||
|
<RecursionField
|
||||||
|
schema={
|
||||||
|
!resourceField
|
||||||
|
? schema
|
||||||
|
: new Schema({
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
[resourceField.name]: {
|
||||||
|
...resourceField.uiSchema,
|
||||||
|
title: undefined,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator-props': {
|
||||||
|
feedbackLayout: 'popover',
|
||||||
|
},
|
||||||
|
'x-decorator': 'FormilyFormItem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
name={ctx.index}
|
||||||
|
onlyRenderProperties
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
// const { fieldName } = props;
|
||||||
|
// const { schema } = useDesignable();
|
||||||
|
// const path = getSchemaPath(schema);
|
||||||
|
// const { addDisplayField } = useDisplayFieldsContext();
|
||||||
|
// const { resource = {} } = useResourceContext();
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (fieldName) {
|
||||||
|
// addDisplayField && addDisplayField(fieldName);
|
||||||
|
// }
|
||||||
|
// }, [fieldName]);
|
||||||
|
// if (!fieldName) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// const field = resource?.fields?.find((item) => item.name === fieldName);
|
||||||
|
// if (!field) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// const name = useContext(FormFieldUIDContext);
|
||||||
|
// const title = schema['title'] || field.uiSchema['title'];
|
||||||
|
// return (
|
||||||
|
// <RecursionField
|
||||||
|
// name={name}
|
||||||
|
// schema={
|
||||||
|
// new Schema({
|
||||||
|
// 'x-path': path,
|
||||||
|
// type: 'void',
|
||||||
|
// properties: {
|
||||||
|
// [field.name]: {
|
||||||
|
// ...field.uiSchema,
|
||||||
|
// title,
|
||||||
|
// 'x-decorator': 'FormilyFormItem',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
});
|
||||||
|
|
||||||
|
Table.Column = observer((props: any) => {
|
||||||
|
const resourceField = useContext(ResourceFieldContext) || {};
|
||||||
|
const { schema, DesignableBar } = useDesignable();
|
||||||
return (
|
return (
|
||||||
<div className={'nb-table-column'}>
|
<div className={'nb-table-column'}>
|
||||||
{field.title}
|
{schema.title || resourceField?.uiSchema?.title}
|
||||||
<DesignableBar />
|
<DesignableBar />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -605,52 +742,28 @@ Table.Column.DesignableBar = () => {
|
|||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={(e) => {
|
onClick={async (e) => {
|
||||||
const title = uid();
|
const title = uid();
|
||||||
field.title = title;
|
field.title = title;
|
||||||
schema.title = title;
|
schema.title = title;
|
||||||
|
refresh();
|
||||||
|
await updateSchema({
|
||||||
|
key: schema['key'],
|
||||||
|
title: title,
|
||||||
|
});
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
点击修改按钮文案
|
编辑列
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
remove();
|
const s = remove();
|
||||||
console.log('Table.Column.DesignableBar', { schema });
|
await removeSchema(s);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除列
|
删除列
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
|
||||||
onClick={() => {
|
|
||||||
const name = uid();
|
|
||||||
insertAfter({
|
|
||||||
name: `column_${name}`,
|
|
||||||
type: 'void',
|
|
||||||
title: `字段 ${name}`,
|
|
||||||
'x-component': 'Table.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
// title: 'z1',
|
|
||||||
},
|
|
||||||
'x-designable-bar': 'Table.Column.DesignableBar',
|
|
||||||
properties: {
|
|
||||||
[name]: {
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
// 'x-read-pretty': true,
|
|
||||||
'x-decorator-props': {
|
|
||||||
feedbackLayout: 'popover',
|
|
||||||
},
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
插入列
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -927,9 +1040,12 @@ Table.Action.DesignableBar = () => {
|
|||||||
{inActionBar ? (
|
{inActionBar ? (
|
||||||
<Menu.Item>放置在右侧</Menu.Item>
|
<Menu.Item>放置在右侧</Menu.Item>
|
||||||
) : (
|
) : (
|
||||||
<Menu.Item>点击表格行时触发 <Switch size={'small'} defaultChecked/></Menu.Item>
|
<Menu.Item>
|
||||||
|
点击表格行时触发
|
||||||
|
<Switch size={'small'} defaultChecked />
|
||||||
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.Item>删除</Menu.Item>
|
<Menu.Item>删除</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
@ -30,9 +30,10 @@ export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) =
|
|||||||
await collection.update(values);
|
await collection.update(values);
|
||||||
}
|
}
|
||||||
await collection.updateAssociations(values);
|
await collection.updateAssociations(values);
|
||||||
|
await collection.migrate();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error.errors', error.errors)
|
console.log('error.errors', error.errors)
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = collection;
|
ctx.body = collection;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Model } from '@nocobase/database';
|
import { Model, TableOptions } from '@nocobase/database';
|
||||||
|
|
||||||
|
export interface LoadOptions {
|
||||||
|
reset?: boolean;
|
||||||
|
where?: any;
|
||||||
|
skipExisting?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MigrateOptions {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
export class Collection extends Model {
|
export class Collection extends Model {
|
||||||
static async create(value?: any, options?: any): Promise<any> {
|
static async create(value?: any, options?: any): Promise<any> {
|
||||||
@ -37,4 +48,66 @@ export class Collection extends Model {
|
|||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOTO:
|
||||||
|
* - database.table 初始化可能存在一些缺陷
|
||||||
|
* - 是否需要考虑关系数据的重载?
|
||||||
|
*
|
||||||
|
* @param opts
|
||||||
|
*/
|
||||||
|
async loadTableOptions(opts: any = {}) {
|
||||||
|
const options = await this.toProps();
|
||||||
|
console.log(JSON.stringify(options, null, 2));
|
||||||
|
const table = this.database.table(options);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 迁移
|
||||||
|
*/
|
||||||
|
async migrate(options: MigrateOptions = {}) {
|
||||||
|
const { isNewRecord } = options;
|
||||||
|
const table = await this.loadTableOptions(options);
|
||||||
|
// 如果不是新增数据,force 必须为 false
|
||||||
|
if (!isNewRecord) {
|
||||||
|
return await table.sync({
|
||||||
|
force: false,
|
||||||
|
alter: {
|
||||||
|
drop: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// TODO: 暂时加了个 collectionSync 解决 collection.create 的数据不清空问题
|
||||||
|
// @ts-ignore
|
||||||
|
const sync = this.sequelize.options.collectionSync;
|
||||||
|
return await table.sync(sync || {
|
||||||
|
force: false,
|
||||||
|
alter: {
|
||||||
|
drop: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO:需要考虑是初次加载还是重载
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
static async load(options: LoadOptions = {}) {
|
||||||
|
const { skipExisting = false, reset = false, where = {}, transaction } = options;
|
||||||
|
const collections = await this.findAll({
|
||||||
|
transaction,
|
||||||
|
where,
|
||||||
|
});
|
||||||
|
for (const collection of collections) {
|
||||||
|
if (skipExisting && this.database.isDefined(collection.get('name'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await collection.loadTableOptions({
|
||||||
|
transaction,
|
||||||
|
reset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ export class Field extends Model {
|
|||||||
const data: any = _.omit(json, ['options', 'created_at', 'updated_at']);
|
const data: any = _.omit(json, ['options', 'created_at', 'updated_at']);
|
||||||
const options = json['options'] || {};
|
const options = json['options'] || {};
|
||||||
const fields = await this.getNestedFields();
|
const fields = await this.getNestedFields();
|
||||||
const props = { ...data, ...options };
|
const props = { type: json['dataType'], ...data, ...options };
|
||||||
if (fields.length) {
|
if (fields.length) {
|
||||||
props['children'] = fields;
|
props['children'] = fields;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user