feat: upgrade formily (#1880)
* feat: upgrade formily * fix: upgrade @formily/json-schema * fix: optimizing performance * fix: performance code * refactor: `React.memo` and `observer` component add `displayName` property * fix: add cache to Schema.compile * feat: 优化代码 * fix: 修复卡顿最终的问题 * Update SchemaComponentProvider.tsx * feat: 再次优化代码 * feat: optimized code --------- Co-authored-by: dream2023 <1098626505@qq.com>
This commit is contained in:
parent
36d16bc015
commit
e6a2a292b3
@ -42,6 +42,7 @@ const HelloProvider = React.memo((props) => {
|
||||
}
|
||||
return <>{props.children}</>
|
||||
});
|
||||
HelloProvider.displayName = 'HelloProvider'
|
||||
|
||||
app.use(HelloProvider);
|
||||
```
|
||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import { Table, TableColumnType } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const ArrayTable = observer((props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
const ArrayTable = observer(
|
||||
(props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
},
|
||||
{ displayName: 'ArrayTable' },
|
||||
);
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -180,11 +180,11 @@ const Hello = observer((props) => {
|
||||
<div style={{ margin: 50 }}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
@ -240,7 +240,7 @@ const useDragEnd = () => {
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
function Draggable(props) {
|
||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||
@ -289,7 +289,7 @@ const Block = observer((props) => {
|
||||
</div>
|
||||
</Droppable>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Block' });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
@ -103,7 +103,7 @@ const SingleText = connect(Input);
|
||||
const UsedObserver = observer((props) => {
|
||||
const form = useForm();
|
||||
return <div>UsedObserver: {form.values.t1}</div>
|
||||
});
|
||||
}, { displayName: 'UsedObserver' });
|
||||
|
||||
const NotUsedObserver = (props) => {
|
||||
const form = useForm();
|
||||
@ -310,7 +310,7 @@ const ArrayList = observer((props) => {
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'ArrayList' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>
|
||||
@ -367,7 +367,7 @@ const ArrayList = observer((props) => {
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'ArrayList' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>
|
||||
@ -432,7 +432,7 @@ const ArrayTable = observer((props: any) => {
|
||||
});
|
||||
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
}, { displayName: 'ArrayTable' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -116,11 +116,11 @@ const Hello = observer((props) => {
|
||||
<div style={{ margin: 50 }}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
|
@ -182,6 +182,7 @@ const HelloProvider = React.memo((props) => {
|
||||
}
|
||||
return <>{props.children}</>
|
||||
});
|
||||
HelloProvider.displayName = 'HelloProvider'
|
||||
|
||||
app.use(HelloProvider);
|
||||
|
||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import { Table, TableColumnType } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const ArrayTable = observer((props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
const ArrayTable = observer(
|
||||
(props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
},
|
||||
{ displayName: 'ArrayTable' },
|
||||
);
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -183,11 +183,11 @@ const Hello = observer((props) => {
|
||||
<div style={{ margin: 50 }}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
@ -243,7 +243,7 @@ const useDragEnd = () => {
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
function Draggable(props) {
|
||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||
@ -292,7 +292,7 @@ const Block = observer((props) => {
|
||||
</div>
|
||||
</Droppable>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Block' });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
@ -103,7 +103,7 @@ const SingleText = connect(Input);
|
||||
const UsedObserver = observer((props) => {
|
||||
const form = useForm();
|
||||
return <div>UsedObserver: {form.values.t1}</div>
|
||||
});
|
||||
}, { displayName: 'UsedObserver' });
|
||||
|
||||
const NotUsedObserver = (props) => {
|
||||
const form = useForm();
|
||||
@ -312,7 +312,7 @@ const ArrayList = observer((props) => {
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'ArrayList' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>
|
||||
@ -369,7 +369,7 @@ const ArrayList = observer((props) => {
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'ArrayList' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>
|
||||
@ -434,7 +434,7 @@ const ArrayTable = observer((props: any) => {
|
||||
});
|
||||
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
}, { displayName: 'ArrayTable' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -116,11 +116,11 @@ const Hello = observer((props) => {
|
||||
<div style={{ margin: 50 }}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
|
@ -42,6 +42,7 @@ const HelloProvider = React.memo((props) => {
|
||||
}
|
||||
return <>{props.children}</>
|
||||
});
|
||||
HelloProvider.displayName = 'HelloProvider'
|
||||
|
||||
app.use(HelloProvider);
|
||||
```
|
||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import { Table, TableColumnType } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const ArrayTable = observer((props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
const ArrayTable = observer(
|
||||
(props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
},
|
||||
{ displayName: 'ArrayTable' },
|
||||
);
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -183,11 +183,11 @@ const Hello = observer((props) => {
|
||||
<div style={{ margin: 50 }}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
@ -243,7 +243,7 @@ const useDragEnd = () => {
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
function Draggable(props) {
|
||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||
@ -292,7 +292,7 @@ const Block = observer((props) => {
|
||||
</div>
|
||||
</Droppable>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Block' });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
@ -103,7 +103,7 @@ const SingleText = connect(Input);
|
||||
const UsedObserver = observer((props) => {
|
||||
const form = useForm();
|
||||
return <div>UsedObserver: {form.values.t1}</div>
|
||||
});
|
||||
}, { displayName: 'UsedObserver' });
|
||||
|
||||
const NotUsedObserver = (props) => {
|
||||
const form = useForm();
|
||||
@ -312,7 +312,7 @@ const ArrayList = observer((props) => {
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'ArrayList' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>
|
||||
@ -369,7 +369,7 @@ const ArrayList = observer((props) => {
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'ArrayList' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>
|
||||
@ -434,7 +434,7 @@ const ArrayTable = observer((props: any) => {
|
||||
});
|
||||
|
||||
return <Table rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
}, { displayName: 'ArrayTable' });
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -116,11 +116,11 @@ const Hello = observer((props) => {
|
||||
<div style={{ margin: 50 }}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
|
@ -176,6 +176,7 @@ const HelloProvider = React.memo((props) => {
|
||||
}
|
||||
return <>{props.children}</>
|
||||
});
|
||||
HelloProvider.displayName = 'HelloProvider'
|
||||
|
||||
app.use(HelloProvider);
|
||||
```
|
||||
|
@ -1,5 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
export default React.memo((props) => {
|
||||
const CommonMemo = React.memo((props) => {
|
||||
return <>{props.children}</>;
|
||||
});
|
||||
CommonMemo.displayName = 'CommonMemo';
|
||||
|
||||
export default CommonMemo;
|
||||
|
@ -374,7 +374,7 @@ const Action: any = observer((props: any) => {
|
||||
<RecursionField basePath={field.address} schema={schema} onlyRenderProperties />
|
||||
</VisibleContext.Provider>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Action' });
|
||||
|
||||
Action.Drawer = observer((props: any) => {
|
||||
const [visible, setVisible] = useContext(VisibleContext);
|
||||
@ -411,13 +411,13 @@ Action.Drawer = observer((props: any) => {
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Action.Drawer' });
|
||||
|
||||
Action.Drawer.Footer = observer((props: any) => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
});
|
||||
}, { displayName: 'Action.Drawer.Footer' });
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
@ -500,7 +500,7 @@ const Hello = observer(({ name }) => {
|
||||
>更新</Button>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
}, { displayName: 'Hello' });
|
||||
|
||||
const RouteSchemaComponent = (props) => {
|
||||
const route = useRoute();
|
||||
@ -678,7 +678,7 @@ const useDragEnd = () => {
|
||||
|
||||
const Page = observer((props) => {
|
||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||
});
|
||||
}, { displayName: 'Page' });
|
||||
|
||||
function Draggable(props) {
|
||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||
@ -727,7 +727,7 @@ const Block = observer((props) => {
|
||||
</div>
|
||||
</Droppable>
|
||||
);
|
||||
});
|
||||
}, { displayName: 'Block' });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
@ -10,9 +10,9 @@
|
||||
"@dnd-kit/core": "^5.0.1",
|
||||
"@dnd-kit/sortable": "^6.0.0",
|
||||
"@emotion/css": "^11.7.1",
|
||||
"@formily/antd": "2.0.20",
|
||||
"@formily/core": "2.0.20",
|
||||
"@formily/react": "2.0.20",
|
||||
"@formily/antd": "2.2.24",
|
||||
"@formily/core": "2.2.24",
|
||||
"@formily/react": "2.2.24",
|
||||
"@nocobase/evaluators": "0.9.4-alpha.2",
|
||||
"@nocobase/sdk": "0.9.4-alpha.2",
|
||||
"@nocobase/utils": "0.9.4-alpha.2",
|
||||
|
@ -61,6 +61,8 @@ const App = React.memo((props: any) => {
|
||||
return <C />;
|
||||
});
|
||||
|
||||
App.displayName = 'App';
|
||||
|
||||
export class Application {
|
||||
providers = [];
|
||||
mainComponent = null;
|
||||
|
@ -3,18 +3,21 @@ import { useDesignable } from '@nocobase/client';
|
||||
import { Button } from 'antd';
|
||||
import { observer } from '@formily/react';
|
||||
|
||||
export const Hello: React.FC<any> = observer(({ name }) => {
|
||||
const { patch, remove } = useDesignable();
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello {name}!</h1>
|
||||
<Button
|
||||
onClick={() => {
|
||||
patch('x-component-props.name', Math.random());
|
||||
}}
|
||||
>
|
||||
更新
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const Hello: React.FC<any> = observer(
|
||||
({ name }) => {
|
||||
const { patch, remove } = useDesignable();
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello {name}!</h1>
|
||||
<Button
|
||||
onClick={() => {
|
||||
patch('x-component-props.name', Math.random());
|
||||
}}
|
||||
>
|
||||
更新
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Hello' },
|
||||
);
|
||||
|
@ -31,36 +31,39 @@ const useDef = (options, props) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const CollectionFieldsTable: React.FC<TableVoidProps> = observer((props) => {
|
||||
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||
const field = useField<Field>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo(() => createForm(), []);
|
||||
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
||||
const result = useDataSource(
|
||||
{
|
||||
uid: fieldSchema['x-uid'],
|
||||
onSuccess(data) {
|
||||
form.setValues({
|
||||
[fieldSchema.name]: data?.data,
|
||||
});
|
||||
export const CollectionFieldsTable: React.FC<TableVoidProps> = observer(
|
||||
(props) => {
|
||||
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||
const field = useField<Field>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo(() => createForm(), []);
|
||||
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
||||
const result = useDataSource(
|
||||
{
|
||||
uid: fieldSchema['x-uid'],
|
||||
onSuccess(data) {
|
||||
form.setValues({
|
||||
[fieldSchema.name]: data?.data,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
props,
|
||||
);
|
||||
return (
|
||||
<AsyncDataProvider value={result}>
|
||||
<FormContext.Provider value={form}>
|
||||
<FieldContext.Provider value={f}>
|
||||
<CollectionFieldsTableArray
|
||||
{...props}
|
||||
rowKey={rowKey}
|
||||
loading={result?.['loading']}
|
||||
useSelectedRowKeys={useSelectedRowKeys}
|
||||
pagination={false}
|
||||
/>
|
||||
</FieldContext.Provider>
|
||||
</FormContext.Provider>
|
||||
</AsyncDataProvider>
|
||||
);
|
||||
});
|
||||
props,
|
||||
);
|
||||
return (
|
||||
<AsyncDataProvider value={result}>
|
||||
<FormContext.Provider value={form}>
|
||||
<FieldContext.Provider value={f}>
|
||||
<CollectionFieldsTableArray
|
||||
{...props}
|
||||
rowKey={rowKey}
|
||||
loading={result?.['loading']}
|
||||
useSelectedRowKeys={useSelectedRowKeys}
|
||||
pagination={false}
|
||||
/>
|
||||
</FieldContext.Provider>
|
||||
</FormContext.Provider>
|
||||
</AsyncDataProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'CollectionFieldsTable' },
|
||||
);
|
||||
|
@ -79,149 +79,150 @@ interface CategorizeDataItem {
|
||||
data: Array<any>;
|
||||
}
|
||||
|
||||
export const CollectionFieldsTableArray: React.FC<any> = observer((props) => {
|
||||
const sortKeyArr: Array<CategorizeKey> = ['primaryAndForeignKey', 'relation', 'basic', 'systemInfo'];
|
||||
const field = useField<ArrayField>();
|
||||
const { name } = useRecord();
|
||||
const { t } = useTranslation();
|
||||
const compile = useCompile();
|
||||
const { getInterface, getInheritCollections, getCollection, getCurrentCollectionFields, getInheritedFields } =
|
||||
useCollectionManager();
|
||||
const {
|
||||
showIndex = true,
|
||||
useSelectedRowKeys = useDef,
|
||||
useDataSource = useDefDataSource,
|
||||
onChange,
|
||||
...others
|
||||
} = props;
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||
const [categorizeData, setCategorizeData] = useState<Array<CategorizeDataItem>>([]);
|
||||
const [expandedKeys, setExpendedKeys] = useState(selectedRowKeys);
|
||||
const inherits = getInheritCollections(name);
|
||||
const currentFields = getCurrentCollectionFields(name);
|
||||
useDataSource({
|
||||
onSuccess(data) {
|
||||
field.value = data?.data || [];
|
||||
const tmpData: Array<CategorizeDataItem> = [];
|
||||
const categorizeMap = new Map<CategorizeKey, any>();
|
||||
const addCategorizeVal = (categorizeKey: CategorizeKey, val) => {
|
||||
let fieldArr = categorizeMap.get(categorizeKey);
|
||||
if (!fieldArr) {
|
||||
fieldArr = [];
|
||||
}
|
||||
fieldArr.push(val);
|
||||
categorizeMap.set(categorizeKey, fieldArr);
|
||||
};
|
||||
field.value.forEach((item) => {
|
||||
const itemInterface = getInterface(item?.interface);
|
||||
if (item?.primaryKey || item.isForeignKey) {
|
||||
addCategorizeVal('primaryAndForeignKey', item);
|
||||
return;
|
||||
}
|
||||
const group = itemInterface?.group as CategorizeKey;
|
||||
switch (group) {
|
||||
case 'systemInfo':
|
||||
case 'relation':
|
||||
addCategorizeVal(group, item);
|
||||
break;
|
||||
default:
|
||||
addCategorizeVal('basic', item);
|
||||
}
|
||||
});
|
||||
if (inherits) {
|
||||
inherits.forEach((v) => {
|
||||
sortKeyArr.push(v);
|
||||
const parentCollection = getCollection(v);
|
||||
parentCollection.fields.map((k) => {
|
||||
if (k.interface) {
|
||||
addCategorizeVal(v, new Proxy(k, {}));
|
||||
field.value.push(new Proxy(k, {}));
|
||||
}
|
||||
});
|
||||
export const CollectionFieldsTableArray: React.FC<any> = observer(
|
||||
(props) => {
|
||||
const sortKeyArr: Array<CategorizeKey> = ['primaryAndForeignKey', 'relation', 'basic', 'systemInfo'];
|
||||
const field = useField<ArrayField>();
|
||||
const { name } = useRecord();
|
||||
const { t } = useTranslation();
|
||||
const compile = useCompile();
|
||||
const { getInterface, getInheritCollections, getCollection, getCurrentCollectionFields, getInheritedFields } =
|
||||
useCollectionManager();
|
||||
const {
|
||||
showIndex = true,
|
||||
useSelectedRowKeys = useDef,
|
||||
useDataSource = useDefDataSource,
|
||||
onChange,
|
||||
...others
|
||||
} = props;
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||
const [categorizeData, setCategorizeData] = useState<Array<CategorizeDataItem>>([]);
|
||||
const [expandedKeys, setExpendedKeys] = useState(selectedRowKeys);
|
||||
const inherits = getInheritCollections(name);
|
||||
const currentFields = getCurrentCollectionFields(name);
|
||||
useDataSource({
|
||||
onSuccess(data) {
|
||||
field.value = data?.data || [];
|
||||
const tmpData: Array<CategorizeDataItem> = [];
|
||||
const categorizeMap = new Map<CategorizeKey, any>();
|
||||
const addCategorizeVal = (categorizeKey: CategorizeKey, val) => {
|
||||
let fieldArr = categorizeMap.get(categorizeKey);
|
||||
if (!fieldArr) {
|
||||
fieldArr = [];
|
||||
}
|
||||
fieldArr.push(val);
|
||||
categorizeMap.set(categorizeKey, fieldArr);
|
||||
};
|
||||
field.value.forEach((item) => {
|
||||
const itemInterface = getInterface(item?.interface);
|
||||
if (item?.primaryKey || item.isForeignKey) {
|
||||
addCategorizeVal('primaryAndForeignKey', item);
|
||||
return;
|
||||
}
|
||||
const group = itemInterface?.group as CategorizeKey;
|
||||
switch (group) {
|
||||
case 'systemInfo':
|
||||
case 'relation':
|
||||
addCategorizeVal(group, item);
|
||||
break;
|
||||
default:
|
||||
addCategorizeVal('basic', item);
|
||||
}
|
||||
});
|
||||
}
|
||||
sortKeyArr.forEach((key) => {
|
||||
if (categorizeMap.get(key)?.length > 0) {
|
||||
const parentCollection = getCollection(key);
|
||||
tmpData.push({
|
||||
key,
|
||||
name:
|
||||
t(CategorizeKeyNameMap.get(key)) ||
|
||||
t(`Parent collection fields`) + `(${compile(parentCollection.title)})`,
|
||||
data: categorizeMap.get(key),
|
||||
if (inherits) {
|
||||
inherits.forEach((v) => {
|
||||
sortKeyArr.push(v);
|
||||
const parentCollection = getCollection(v);
|
||||
parentCollection.fields.map((k) => {
|
||||
if (k.interface) {
|
||||
addCategorizeVal(v, new Proxy(k, {}));
|
||||
field.value.push(new Proxy(k, {}));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
setExpendedKeys(sortKeyArr);
|
||||
setCategorizeData(tmpData);
|
||||
},
|
||||
});
|
||||
const useTableColumns = () => {
|
||||
const schema = useFieldSchema();
|
||||
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
||||
const columns = schema
|
||||
.reduceProperties((buf, s) => {
|
||||
if (isColumnComponent(s)) {
|
||||
return buf.concat([s]);
|
||||
}
|
||||
}, [])
|
||||
.map((s: Schema) => {
|
||||
return {
|
||||
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
||||
dataIndex: s.name,
|
||||
key: s.name,
|
||||
render: (v, record) => {
|
||||
const index = findIndex(field.value, record);
|
||||
return (
|
||||
<RecordIndexProvider index={index}>
|
||||
<RecordProvider record={record}>
|
||||
<RecursionField schema={s} name={index} onlyRenderProperties />
|
||||
</RecordProvider>
|
||||
</RecordIndexProvider>
|
||||
);
|
||||
},
|
||||
} as TableColumnProps<any>;
|
||||
});
|
||||
if (!exists) {
|
||||
return columns;
|
||||
}
|
||||
return columns.concat({
|
||||
title: render(),
|
||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
||||
key: 'TABLE_COLUMN_INITIALIZER',
|
||||
sortKeyArr.forEach((key) => {
|
||||
if (categorizeMap.get(key)?.length > 0) {
|
||||
const parentCollection = getCollection(key);
|
||||
tmpData.push({
|
||||
key,
|
||||
name:
|
||||
t(CategorizeKeyNameMap.get(key)) ||
|
||||
t(`Parent collection fields`) + `(${compile(parentCollection.title)})`,
|
||||
data: categorizeMap.get(key),
|
||||
});
|
||||
}
|
||||
});
|
||||
setExpendedKeys(sortKeyArr);
|
||||
setCategorizeData(tmpData);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const ExpandedRowRender = (record: CategorizeDataItem, index, indent, expanded) => {
|
||||
const columns = useTableColumns();
|
||||
if (!props.loading) {
|
||||
if (inherits.includes(record.key)) {
|
||||
columns.pop();
|
||||
columns.push({
|
||||
title: <RecursionField name={'column4'} schema={overridingSchema as Schema} onlyRenderSelf />,
|
||||
dataIndex: 'column4',
|
||||
key: 'column4',
|
||||
render: (v, record) => {
|
||||
const index = findIndex(field.value, record);
|
||||
return (
|
||||
<RecordIndexProvider index={index}>
|
||||
<RecordProvider record={record}>
|
||||
<SchemaComponent
|
||||
scope={{ currentCollection: name }}
|
||||
schema={overridingSchema as Schema}
|
||||
name={index}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</RecordProvider>
|
||||
</RecordIndexProvider>
|
||||
);
|
||||
},
|
||||
const useTableColumns = () => {
|
||||
const schema = useFieldSchema();
|
||||
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
||||
const columns = schema
|
||||
.reduceProperties((buf, s) => {
|
||||
if (isColumnComponent(s)) {
|
||||
return buf.concat([s]);
|
||||
}
|
||||
}, [])
|
||||
.map((s: Schema) => {
|
||||
return {
|
||||
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
||||
dataIndex: s.name,
|
||||
key: s.name,
|
||||
render: (v, record) => {
|
||||
const index = findIndex(field.value, record);
|
||||
return (
|
||||
<RecordIndexProvider index={index}>
|
||||
<RecordProvider record={record}>
|
||||
<RecursionField schema={s} name={index} onlyRenderProperties />
|
||||
</RecordProvider>
|
||||
</RecordIndexProvider>
|
||||
);
|
||||
},
|
||||
} as TableColumnProps<any>;
|
||||
});
|
||||
if (!exists) {
|
||||
return columns;
|
||||
}
|
||||
const restProps = {
|
||||
rowSelection:
|
||||
props.rowSelection && !inherits.includes(record.key)
|
||||
? {
|
||||
return columns.concat({
|
||||
title: render(),
|
||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
||||
key: 'TABLE_COLUMN_INITIALIZER',
|
||||
});
|
||||
};
|
||||
|
||||
const ExpandedRowRender = (record: CategorizeDataItem, index, indent, expanded) => {
|
||||
const columns = useTableColumns();
|
||||
if (!props.loading) {
|
||||
if (inherits.includes(record.key)) {
|
||||
columns.pop();
|
||||
columns.push({
|
||||
title: <RecursionField name={'column4'} schema={overridingSchema as Schema} onlyRenderSelf />,
|
||||
dataIndex: 'column4',
|
||||
key: 'column4',
|
||||
render: (v, record) => {
|
||||
const index = findIndex(field.value, record);
|
||||
return (
|
||||
<RecordIndexProvider index={index}>
|
||||
<RecordProvider record={record}>
|
||||
<SchemaComponent
|
||||
scope={{ currentCollection: name }}
|
||||
schema={overridingSchema as Schema}
|
||||
name={index}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</RecordProvider>
|
||||
</RecordIndexProvider>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
const restProps = {
|
||||
rowSelection:
|
||||
props.rowSelection && !inherits.includes(record.key)
|
||||
? {
|
||||
type: 'checkbox',
|
||||
selectedRowKeys,
|
||||
onChange(selectedRowKeys: any[]) {
|
||||
@ -229,52 +230,54 @@ export const CollectionFieldsTableArray: React.FC<any> = observer((props) => {
|
||||
},
|
||||
...props.rowSelection,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
return (
|
||||
<Table
|
||||
{...others}
|
||||
{...restProps}
|
||||
components={components}
|
||||
showHeader={true}
|
||||
columns={columns}
|
||||
dataSource={record.data}
|
||||
pagination={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
.ant-table {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Table
|
||||
showHeader={false}
|
||||
loading={props?.loading}
|
||||
columns={groupColumns}
|
||||
dataSource={categorizeData}
|
||||
pagination={false}
|
||||
expandable={{
|
||||
expandedRowRender: ExpandedRowRender,
|
||||
expandedRowKeys: expandedKeys,
|
||||
}}
|
||||
onExpand={(expanded, record) => {
|
||||
let keys = [];
|
||||
if (expanded) {
|
||||
keys = expandedKeys.concat([record.key]);
|
||||
} else {
|
||||
keys = expandedKeys.filter((v) => {
|
||||
return v !== record.key;
|
||||
});
|
||||
: undefined,
|
||||
};
|
||||
return (
|
||||
<Table
|
||||
{...others}
|
||||
{...restProps}
|
||||
components={components}
|
||||
showHeader={true}
|
||||
columns={columns}
|
||||
dataSource={record.data}
|
||||
pagination={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
.ant-table {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
setExpendedKeys(keys);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
`}
|
||||
>
|
||||
<Table
|
||||
showHeader={false}
|
||||
loading={props?.loading}
|
||||
columns={groupColumns}
|
||||
dataSource={categorizeData}
|
||||
pagination={false}
|
||||
expandable={{
|
||||
expandedRowRender: ExpandedRowRender,
|
||||
expandedRowKeys: expandedKeys,
|
||||
}}
|
||||
onExpand={(expanded, record) => {
|
||||
let keys = [];
|
||||
if (expanded) {
|
||||
keys = expandedKeys.concat([record.key]);
|
||||
} else {
|
||||
keys = expandedKeys.filter((v) => {
|
||||
return v !== record.key;
|
||||
});
|
||||
}
|
||||
setExpendedKeys(keys);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'CollectionFieldsTableArray' },
|
||||
);
|
||||
|
@ -50,17 +50,20 @@ function Droppable(props) {
|
||||
);
|
||||
}
|
||||
|
||||
const TabTitle = observer(({ item }: { item: any }) => {
|
||||
return (
|
||||
<Droppable id={item.id.toString()} data={item}>
|
||||
<div>
|
||||
<Draggable id={item.id.toString()} data={item}>
|
||||
<TabBar item={item} />
|
||||
</Draggable>
|
||||
</div>
|
||||
</Droppable>
|
||||
);
|
||||
});
|
||||
const TabTitle = observer(
|
||||
({ item }: { item: any }) => {
|
||||
return (
|
||||
<Droppable id={item.id.toString()} data={item}>
|
||||
<div>
|
||||
<Draggable id={item.id.toString()} data={item}>
|
||||
<TabBar item={item} />
|
||||
</Draggable>
|
||||
</div>
|
||||
</Droppable>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TabTitle' },
|
||||
);
|
||||
|
||||
const TabBar = ({ item }) => {
|
||||
const compile = useCompile();
|
||||
@ -71,45 +74,48 @@ const TabBar = ({ item }) => {
|
||||
</span>
|
||||
);
|
||||
};
|
||||
const DndProvider = observer((props) => {
|
||||
const [activeTab, setActiveId] = useState(null);
|
||||
const { refresh } = useContext(CollectionCategroriesContext);
|
||||
const { refresh: refreshCM } = useResourceActionContext();
|
||||
const api = useAPIClient();
|
||||
const onDragEnd = async (props: DragEndEvent) => {
|
||||
const { active, over } = props;
|
||||
setTimeout(() => {
|
||||
setActiveId(null);
|
||||
});
|
||||
if (over && over.id !== active.id) {
|
||||
await api.resource('collectionCategories').move({
|
||||
sourceId: active.id,
|
||||
targetId: over.id,
|
||||
const DndProvider = observer(
|
||||
(props) => {
|
||||
const [activeTab, setActiveId] = useState(null);
|
||||
const { refresh } = useContext(CollectionCategroriesContext);
|
||||
const { refresh: refreshCM } = useResourceActionContext();
|
||||
const api = useAPIClient();
|
||||
const onDragEnd = async (props: DragEndEvent) => {
|
||||
const { active, over } = props;
|
||||
setTimeout(() => {
|
||||
setActiveId(null);
|
||||
});
|
||||
await refresh();
|
||||
await refreshCM();
|
||||
if (over && over.id !== active.id) {
|
||||
await api.resource('collectionCategories').move({
|
||||
sourceId: active.id,
|
||||
targetId: over.id,
|
||||
});
|
||||
await refresh();
|
||||
await refreshCM();
|
||||
}
|
||||
};
|
||||
|
||||
function onDragStart(event) {
|
||||
setActiveId(event.active?.data.current);
|
||||
}
|
||||
};
|
||||
|
||||
function onDragStart(event) {
|
||||
setActiveId(event.active?.data.current);
|
||||
}
|
||||
|
||||
const mouseSensor = useSensor(MouseSensor, {
|
||||
activationConstraint: {
|
||||
distance: 10,
|
||||
},
|
||||
});
|
||||
const sensors = useSensors(mouseSensor);
|
||||
return (
|
||||
<DndContext sensors={sensors} onDragEnd={onDragEnd} onDragStart={onDragStart}>
|
||||
{props.children}
|
||||
<DragOverlay>
|
||||
{activeTab ? <span style={{ whiteSpace: 'nowrap' }}>{<TabBar item={activeTab} />}</span> : null}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
);
|
||||
});
|
||||
const mouseSensor = useSensor(MouseSensor, {
|
||||
activationConstraint: {
|
||||
distance: 10,
|
||||
},
|
||||
});
|
||||
const sensors = useSensors(mouseSensor);
|
||||
return (
|
||||
<DndContext sensors={sensors} onDragEnd={onDragEnd} onDragStart={onDragStart}>
|
||||
{props.children}
|
||||
<DragOverlay>
|
||||
{activeTab ? <span style={{ whiteSpace: 'nowrap' }}>{<TabBar item={activeTab} />}</span> : null}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
);
|
||||
},
|
||||
{ displayName: 'DndProvider' },
|
||||
);
|
||||
export const ConfigurationTabs = () => {
|
||||
const { data, refresh } = useContext(CollectionCategroriesContext);
|
||||
const { refresh: refreshCM, run, defaultRequest, setState } = useResourceActionContext();
|
||||
|
@ -3,14 +3,17 @@ import { Tag } from 'antd';
|
||||
import React from 'react';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
|
||||
export const CollectionCategory = observer((props: any) => {
|
||||
const { value } = props;
|
||||
const compile = useCompile();
|
||||
return (
|
||||
<>
|
||||
{value.map((item) => {
|
||||
return <Tag color={item.color}>{compile(item?.name)}</Tag>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
});
|
||||
export const CollectionCategory = observer(
|
||||
(props: any) => {
|
||||
const { value } = props;
|
||||
const compile = useCompile();
|
||||
return (
|
||||
<>
|
||||
{value.map((item) => {
|
||||
return <Tag color={item.color}>{compile(item?.name)}</Tag>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'CollectionCategory' },
|
||||
);
|
||||
|
@ -4,13 +4,16 @@ import React from 'react';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
import { useCollectionManager } from '../../hooks';
|
||||
|
||||
export const CollectionFieldInterface = observer((props: any) => {
|
||||
const { value } = props;
|
||||
const { getInterface } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const schema = getInterface(value);
|
||||
export const CollectionFieldInterface = observer(
|
||||
(props: any) => {
|
||||
const { value } = props;
|
||||
const { getInterface } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const schema = getInterface(value);
|
||||
|
||||
if (!schema) return null;
|
||||
if (!schema) return null;
|
||||
|
||||
return <Tag>{compile(schema.title)}</Tag>;
|
||||
});
|
||||
return <Tag>{compile(schema.title)}</Tag>;
|
||||
},
|
||||
{ displayName: 'CollectionFieldInterface' },
|
||||
);
|
||||
|
@ -4,11 +4,14 @@ import React from 'react';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
import { useCollectionManager } from '../../hooks';
|
||||
|
||||
export const CollectionTemplate = observer((props: any) => {
|
||||
const { value } = props;
|
||||
const { getTemplate } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const schema = getTemplate(value);
|
||||
export const CollectionTemplate = observer(
|
||||
(props: any) => {
|
||||
const { value } = props;
|
||||
const { getTemplate } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const schema = getTemplate(value);
|
||||
|
||||
return <Tag>{compile(schema?.title || '{{t("General collection")}}')}</Tag>;
|
||||
});
|
||||
return <Tag>{compile(schema?.title || '{{t("General collection")}}')}</Tag>;
|
||||
},
|
||||
{ displayName: 'CollectionTemplate' },
|
||||
);
|
||||
|
@ -6,36 +6,39 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
import { useCollectionManager } from '../../hooks';
|
||||
|
||||
export const FieldSummary = observer((props: any) => {
|
||||
const { schemaKey } = props;
|
||||
const { getInterface } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const schema = getInterface(schemaKey);
|
||||
export const FieldSummary = observer(
|
||||
(props: any) => {
|
||||
const { schemaKey } = props;
|
||||
const { getInterface } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const schema = getInterface(schemaKey);
|
||||
|
||||
if (!schema) return null;
|
||||
if (!schema) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
background: #f6f6f6;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
`}
|
||||
>
|
||||
<div className={css``}>
|
||||
{t('Field interface')}: <Tag>{compile(schema.title)}</Tag>
|
||||
</div>
|
||||
{schema.description ? (
|
||||
<div
|
||||
className={css`
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
`}
|
||||
>
|
||||
{compile(schema.description)}
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
background: #f6f6f6;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
`}
|
||||
>
|
||||
<div className={css``}>
|
||||
{t('Field interface')}: <Tag>{compile(schema.title)}</Tag>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
{schema.description ? (
|
||||
<div
|
||||
className={css`
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
`}
|
||||
>
|
||||
{compile(schema.description)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'FieldSummary' },
|
||||
);
|
||||
|
@ -6,36 +6,39 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
import { useCollectionManager } from '../../hooks';
|
||||
|
||||
export const TemplateSummay = observer((props: any) => {
|
||||
const { schemaKey } = props;
|
||||
const { getTemplate } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const schema = getTemplate(schemaKey);
|
||||
export const TemplateSummay = observer(
|
||||
(props: any) => {
|
||||
const { schemaKey } = props;
|
||||
const { getTemplate } = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const schema = getTemplate(schemaKey);
|
||||
|
||||
if (!schema) return null;
|
||||
if (!schema) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
background: #f6f6f6;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
`}
|
||||
>
|
||||
<div className={css``}>
|
||||
{t('Collection template')}: <Tag>{compile(schema.title)}</Tag>
|
||||
</div>
|
||||
{schema.description ? (
|
||||
<div
|
||||
className={css`
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
`}
|
||||
>
|
||||
{compile(schema.description)}
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
background: #f6f6f6;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
`}
|
||||
>
|
||||
<div className={css``}>
|
||||
{t('Collection template')}: <Tag>{compile(schema.title)}</Tag>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
{schema.description ? (
|
||||
<div
|
||||
className={css`
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
`}
|
||||
>
|
||||
{compile(schema.description)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TemplateSummay' },
|
||||
);
|
||||
|
@ -6,109 +6,127 @@ import { useRecord } from '../../../record-provider';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
import { useCollectionManager } from '../../hooks';
|
||||
|
||||
export const SourceForeignKey = observer(() => {
|
||||
const record = useRecord();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
||||
const field = useField<Field>();
|
||||
const form = useForm();
|
||||
const { getCollectionFields } = useCollectionManager();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder={'留空时,自动生成 FK 字段'}
|
||||
disabled={field.disabled}
|
||||
value={field.value}
|
||||
options={getCollectionFields(collection.name)
|
||||
.filter((field) => field.type)
|
||||
.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title || field.name,
|
||||
value: field.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const SourceForeignKey = observer(
|
||||
() => {
|
||||
const record = useRecord();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
||||
const field = useField<Field>();
|
||||
const form = useForm();
|
||||
const { getCollectionFields } = useCollectionManager();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder={'留空时,自动生成 FK 字段'}
|
||||
disabled={field.disabled}
|
||||
value={field.value}
|
||||
options={getCollectionFields(collection.name)
|
||||
.filter((field) => field.type)
|
||||
.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title || field.name,
|
||||
value: field.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'SourceForeignKey' },
|
||||
);
|
||||
|
||||
export const ThroughForeignKey = observer(() => {
|
||||
const field = useField<Field>();
|
||||
const form = useForm();
|
||||
const { getCollectionFields } = useCollectionManager();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder={'留空时,自动生成 FK 字段'}
|
||||
disabled={field.disabled}
|
||||
value={field.value}
|
||||
options={getCollectionFields(form.values.through)
|
||||
.filter((field) => field.type)
|
||||
.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title || field.name,
|
||||
value: field.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const ThroughForeignKey = observer(
|
||||
() => {
|
||||
const field = useField<Field>();
|
||||
const form = useForm();
|
||||
const { getCollectionFields } = useCollectionManager();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder={'留空时,自动生成 FK 字段'}
|
||||
disabled={field.disabled}
|
||||
value={field.value}
|
||||
options={getCollectionFields(form.values.through)
|
||||
.filter((field) => field.type)
|
||||
.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title || field.name,
|
||||
value: field.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ThroughForeignKey' },
|
||||
);
|
||||
|
||||
export const TargetForeignKey = observer(() => {
|
||||
const field = useField<Field>();
|
||||
const form = useForm();
|
||||
const { getCollectionFields } = useCollectionManager();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder={'留空时,自动生成 FK 字段'}
|
||||
disabled={field.disabled}
|
||||
value={field.value}
|
||||
options={getCollectionFields(form.values.target)
|
||||
.filter((field) => field.type)
|
||||
.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title || field.name,
|
||||
value: field.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const TargetForeignKey = observer(
|
||||
() => {
|
||||
const field = useField<Field>();
|
||||
const form = useForm();
|
||||
const { getCollectionFields } = useCollectionManager();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder={'留空时,自动生成 FK 字段'}
|
||||
disabled={field.disabled}
|
||||
value={field.value}
|
||||
options={getCollectionFields(form.values.target)
|
||||
.filter((field) => field.type)
|
||||
.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title || field.name,
|
||||
value: field.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TargetForeignKey' },
|
||||
);
|
||||
|
||||
export const SourceCollection = observer(() => {
|
||||
const record = useRecord();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
||||
const compile = useCompile();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
disabled
|
||||
value={collection.name}
|
||||
options={[{ value: collection.name, label: compile(collection.title) }]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const SourceCollection = observer(
|
||||
() => {
|
||||
const record = useRecord();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
||||
const compile = useCompile();
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
disabled
|
||||
value={collection.name}
|
||||
options={[{ value: collection.name, label: compile(collection.title) }]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'SourceCollection' },
|
||||
);
|
||||
|
||||
export const SourceKey = observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const SourceKey = observer(
|
||||
() => {
|
||||
return (
|
||||
<div>
|
||||
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'SourceKey' },
|
||||
);
|
||||
|
||||
export const TargetKey = observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const TargetKey = observer(
|
||||
() => {
|
||||
return (
|
||||
<div>
|
||||
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TargetKey' },
|
||||
);
|
||||
|
@ -166,107 +166,113 @@ export const ds = {
|
||||
useDestroyAction,
|
||||
};
|
||||
|
||||
export const SubFieldDataSourceProvider = observer((props) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const record = useRecord();
|
||||
const api = useAPIClient();
|
||||
const service = useRequest(
|
||||
() => {
|
||||
if (record.interface === 'subTable') {
|
||||
if (record?.children?.length) {
|
||||
return Promise.resolve({
|
||||
data: record?.children || [],
|
||||
});
|
||||
export const SubFieldDataSourceProvider = observer(
|
||||
(props) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const record = useRecord();
|
||||
const api = useAPIClient();
|
||||
const service = useRequest(
|
||||
() => {
|
||||
if (record.interface === 'subTable') {
|
||||
if (record?.children?.length) {
|
||||
return Promise.resolve({
|
||||
data: record?.children || [],
|
||||
});
|
||||
}
|
||||
return api
|
||||
.resource('fields')
|
||||
.list({
|
||||
paginate: false,
|
||||
// appends: ['uiSchema'],
|
||||
sort: 'sort',
|
||||
filter: {
|
||||
parentKey: record.key,
|
||||
},
|
||||
})
|
||||
.then((res) => res?.data);
|
||||
}
|
||||
return api
|
||||
.resource('fields')
|
||||
.list({
|
||||
paginate: false,
|
||||
// appends: ['uiSchema'],
|
||||
sort: 'sort',
|
||||
filter: {
|
||||
parentKey: record.key,
|
||||
},
|
||||
})
|
||||
.then((res) => res?.data);
|
||||
}
|
||||
return Promise.resolve({
|
||||
data: [],
|
||||
});
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
console.log('dataSource1', data?.data);
|
||||
setDataSource(data?.data);
|
||||
return Promise.resolve({
|
||||
data: [],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return (
|
||||
<CollectionProvider collection={collection}>
|
||||
<DataSourceContext.Provider
|
||||
value={{
|
||||
rowKey: 'name',
|
||||
service,
|
||||
dataSource,
|
||||
setDataSource,
|
||||
selectedRowKeys,
|
||||
setSelectedRowKeys,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DataSourceContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
});
|
||||
{
|
||||
onSuccess(data) {
|
||||
console.log('dataSource1', data?.data);
|
||||
setDataSource(data?.data);
|
||||
},
|
||||
},
|
||||
);
|
||||
return (
|
||||
<CollectionProvider collection={collection}>
|
||||
<DataSourceContext.Provider
|
||||
value={{
|
||||
rowKey: 'name',
|
||||
service,
|
||||
dataSource,
|
||||
setDataSource,
|
||||
selectedRowKeys,
|
||||
setSelectedRowKeys,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DataSourceContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'SubFieldDataSourceProvider' },
|
||||
);
|
||||
|
||||
export const DataSourceProvider = observer((props: any) => {
|
||||
const { rowKey, collection, association } = props;
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const record = useRecord();
|
||||
const api = useAPIClient();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const coll = getCollection(collection);
|
||||
const resourceOf = record?.[association.targetKey || 'id'];
|
||||
const service = useRequest(
|
||||
() => {
|
||||
if (resourceOf) {
|
||||
return api
|
||||
.request({
|
||||
resource: `${association.collectionName}.${association.name}`,
|
||||
resourceOf,
|
||||
action: 'list',
|
||||
params: {
|
||||
appends: coll?.fields?.filter((field) => field.target)?.map((field) => field.name),
|
||||
},
|
||||
})
|
||||
.then((res) => res.data);
|
||||
}
|
||||
return Promise.resolve({
|
||||
data: record?.[association.name] || [],
|
||||
});
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
setDataSource(data?.data);
|
||||
export const DataSourceProvider = observer(
|
||||
(props: any) => {
|
||||
const { rowKey, collection, association } = props;
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const record = useRecord();
|
||||
const api = useAPIClient();
|
||||
const { getCollection } = useCollectionManager();
|
||||
const coll = getCollection(collection);
|
||||
const resourceOf = record?.[association.targetKey || 'id'];
|
||||
const service = useRequest(
|
||||
() => {
|
||||
if (resourceOf) {
|
||||
return api
|
||||
.request({
|
||||
resource: `${association.collectionName}.${association.name}`,
|
||||
resourceOf,
|
||||
action: 'list',
|
||||
params: {
|
||||
appends: coll?.fields?.filter((field) => field.target)?.map((field) => field.name),
|
||||
},
|
||||
})
|
||||
.then((res) => res.data);
|
||||
}
|
||||
return Promise.resolve({
|
||||
data: record?.[association.name] || [],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return (
|
||||
<CollectionProvider collection={coll}>
|
||||
<DataSourceContext.Provider
|
||||
value={{
|
||||
rowKey,
|
||||
service,
|
||||
dataSource,
|
||||
setDataSource,
|
||||
selectedRowKeys,
|
||||
setSelectedRowKeys,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DataSourceContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
});
|
||||
{
|
||||
onSuccess(data) {
|
||||
setDataSource(data?.data);
|
||||
},
|
||||
},
|
||||
);
|
||||
return (
|
||||
<CollectionProvider collection={coll}>
|
||||
<DataSourceContext.Provider
|
||||
value={{
|
||||
rowKey,
|
||||
service,
|
||||
dataSource,
|
||||
setDataSource,
|
||||
selectedRowKeys,
|
||||
setSelectedRowKeys,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DataSourceContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'DataSourceProvider' },
|
||||
);
|
||||
|
@ -217,3 +217,4 @@ function areEqual(prevProps, nextProps) {
|
||||
}
|
||||
|
||||
export const PreviewFields = React.memo(PreviewCom, areEqual);
|
||||
PreviewFields.displayName = 'PreviewFields';
|
||||
|
@ -6,21 +6,27 @@ import { ActionModal } from './Action.Modal';
|
||||
import { ActionPage } from './Action.Page';
|
||||
import { ComposedActionDrawer } from './types';
|
||||
|
||||
export const ActionContainer: ComposedActionDrawer = observer((props: any) => {
|
||||
const { openMode } = useActionContext();
|
||||
if (openMode === 'drawer') {
|
||||
return <ActionDrawer footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||
}
|
||||
if (openMode === 'modal') {
|
||||
return <ActionModal footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||
}
|
||||
return <ActionPage footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||
});
|
||||
export const ActionContainer: ComposedActionDrawer = observer(
|
||||
(props: any) => {
|
||||
const { openMode } = useActionContext();
|
||||
if (openMode === 'drawer') {
|
||||
return <ActionDrawer footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||
}
|
||||
if (openMode === 'modal') {
|
||||
return <ActionModal footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||
}
|
||||
return <ActionPage footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||
},
|
||||
{ displayName: 'ActionContainer' },
|
||||
);
|
||||
|
||||
ActionContainer.Footer = observer(() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
});
|
||||
ActionContainer.Footer = observer(
|
||||
() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
},
|
||||
{ displayName: 'ActionContainer.Footer' },
|
||||
);
|
||||
|
||||
export default ActionContainer;
|
||||
|
@ -14,111 +14,117 @@ const openSizeWidthMap = new Map<OpenSize, string>([
|
||||
['middle', '50%'],
|
||||
['large', '70%'],
|
||||
]);
|
||||
export const ActionDrawer: ComposedActionDrawer = observer((props) => {
|
||||
const { footerNodeName = 'Action.Drawer.Footer', ...others } = props;
|
||||
const { t } = useTranslation();
|
||||
const { visible, setVisible, openSize = 'middle' } = useActionContext();
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const openSizeFromParent = schema.parent?.['x-component-props']?.['openSize'];
|
||||
const finalOpenSize = openSizeFromParent || openSize;
|
||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{createPortal(
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Drawer
|
||||
width={openSizeWidthMap.get(finalOpenSize)}
|
||||
title={field.title}
|
||||
{...others}
|
||||
destroyOnClose
|
||||
open={visible}
|
||||
onClose={() => setVisible(false, true)}
|
||||
className={classNames(
|
||||
others.className,
|
||||
css`
|
||||
&.nb-action-popup {
|
||||
.ant-drawer-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-drawer-body {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.ant-drawer-content {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
}
|
||||
|
||||
&.nb-record-picker-selector {
|
||||
.nb-block-item {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.general-schema-designer {
|
||||
top: -8px;
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
right: -8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
footer={
|
||||
footerSchema && (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export const ActionDrawer: ComposedActionDrawer = observer(
|
||||
(props) => {
|
||||
const { footerNodeName = 'Action.Drawer.Footer', ...others } = props;
|
||||
const { t } = useTranslation();
|
||||
const { visible, setVisible, openSize = 'middle' } = useActionContext();
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const openSizeFromParent = schema.parent?.['x-component-props']?.['openSize'];
|
||||
const finalOpenSize = openSizeFromParent || openSize;
|
||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{createPortal(
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
<Drawer
|
||||
width={openSizeWidthMap.get(finalOpenSize)}
|
||||
title={field.title}
|
||||
{...others}
|
||||
destroyOnClose
|
||||
open={visible}
|
||||
onClose={() => setVisible(false, true)}
|
||||
className={classNames(
|
||||
others.className,
|
||||
css`
|
||||
&.nb-action-popup {
|
||||
.ant-drawer-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ActionDrawer.Footer = observer(() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
});
|
||||
.ant-drawer-body {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.ant-drawer-content {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
}
|
||||
|
||||
&.nb-record-picker-selector {
|
||||
.nb-block-item {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.general-schema-designer {
|
||||
top: -8px;
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
right: -8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
footer={
|
||||
footerSchema && (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ActionDrawer' },
|
||||
);
|
||||
|
||||
ActionDrawer.Footer = observer(
|
||||
() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
},
|
||||
{ displayName: 'ActionDrawer.Footer' },
|
||||
);
|
||||
|
||||
export default ActionDrawer;
|
||||
|
@ -3,6 +3,9 @@ import React from 'react';
|
||||
import Action from './Action';
|
||||
import { ComposedAction } from './types';
|
||||
|
||||
export const ActionLink: ComposedAction = observer((props: any) => {
|
||||
return <Action {...props} component={'a'} className={'nb-action-link'} />;
|
||||
});
|
||||
export const ActionLink: ComposedAction = observer(
|
||||
(props: any) => {
|
||||
return <Action {...props} component={'a'} className={'nb-action-link'} />;
|
||||
},
|
||||
{ displayName: 'ActionLink' },
|
||||
);
|
||||
|
@ -12,104 +12,110 @@ const openSizeWidthMap = new Map<OpenSize, string>([
|
||||
['middle', '60%'],
|
||||
['large', '80%'],
|
||||
]);
|
||||
export const ActionModal: ComposedActionDrawer<ModalProps> = observer((props) => {
|
||||
const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props;
|
||||
const { visible, setVisible, openSize = 'large' } = useActionContext();
|
||||
const actualWidth = width ?? openSizeWidthMap.get(openSize);
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{createPortal(
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Modal
|
||||
width={actualWidth}
|
||||
title={field.title}
|
||||
{...(others as ModalProps)}
|
||||
destroyOnClose
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false, true)}
|
||||
className={classNames(
|
||||
others.className,
|
||||
css`
|
||||
&.nb-action-popup {
|
||||
.ant-modal-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.ant-modal-close-x {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
footer={
|
||||
footerSchema ? (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
false
|
||||
)
|
||||
}
|
||||
export const ActionModal: ComposedActionDrawer<ModalProps> = observer(
|
||||
(props) => {
|
||||
const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props;
|
||||
const { visible, setVisible, openSize = 'large' } = useActionContext();
|
||||
const actualWidth = width ?? openSizeWidthMap.get(openSize);
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{createPortal(
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
<Modal
|
||||
width={actualWidth}
|
||||
title={field.title}
|
||||
{...(others as ModalProps)}
|
||||
destroyOnClose
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false, true)}
|
||||
className={classNames(
|
||||
others.className,
|
||||
css`
|
||||
&.nb-action-popup {
|
||||
.ant-modal-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ActionModal.Footer = observer(() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
});
|
||||
.ant-modal-body {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.ant-modal-close-x {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
footer={
|
||||
footerSchema ? (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
false
|
||||
)
|
||||
}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ActionModal' },
|
||||
);
|
||||
|
||||
ActionModal.Footer = observer(
|
||||
() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
},
|
||||
{ displayName: 'ActionModal.Footer' },
|
||||
);
|
||||
|
||||
export default ActionModal;
|
||||
|
@ -10,65 +10,71 @@ const useScope = (key: string) => {
|
||||
return scope[key];
|
||||
};
|
||||
|
||||
export const ActionPage: ComposedActionDrawer = observer((props: any) => {
|
||||
const { footerNodeName = 'Action.Page.Footer', ...others } = props;
|
||||
const { containerRefKey, visible, setVisible } = useActionContext();
|
||||
const containerRef = useScope(containerRefKey);
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{containerRef?.current &&
|
||||
visible &&
|
||||
createPortal(
|
||||
<div>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
{footerSchema && (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
/* justify-content: flex-end; */
|
||||
/* flex-direction: row-reverse; */
|
||||
width: 100%;
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>,
|
||||
containerRef?.current,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
export const ActionPage: ComposedActionDrawer = observer(
|
||||
(props: any) => {
|
||||
const { footerNodeName = 'Action.Page.Footer', ...others } = props;
|
||||
const { containerRefKey, visible, setVisible } = useActionContext();
|
||||
const containerRef = useScope(containerRefKey);
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{containerRef?.current &&
|
||||
visible &&
|
||||
createPortal(
|
||||
<div>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
{footerSchema && (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
/* justify-content: flex-end; */
|
||||
/* flex-direction: row-reverse; */
|
||||
width: 100%;
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<RecursionField
|
||||
basePath={field.address}
|
||||
schema={schema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === footerNodeName;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>,
|
||||
containerRef?.current,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ActionPage' },
|
||||
);
|
||||
|
||||
ActionPage.Footer = observer(() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
});
|
||||
ActionPage.Footer = observer(
|
||||
() => {
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||
},
|
||||
{ displayName: 'ActionPage.Footer' },
|
||||
);
|
||||
|
||||
export default ActionPage;
|
||||
|
@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
||||
import { observer, RecursionField, useField, useFieldSchema, useForm } from '@formily/react';
|
||||
import { Button, Modal, Popover } from 'antd';
|
||||
import classnames from 'classnames';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useActionContext } from '../..';
|
||||
import { useDesignable } from '../../';
|
||||
import { Icon } from '../../../icon';
|
||||
@ -64,139 +64,158 @@ export const actionDesignerCss = css`
|
||||
}
|
||||
`;
|
||||
|
||||
export const Action: ComposedAction = observer((props: any) => {
|
||||
const {
|
||||
popover,
|
||||
confirm,
|
||||
openMode: om,
|
||||
containerRefKey,
|
||||
component,
|
||||
useAction = useA,
|
||||
className,
|
||||
icon,
|
||||
title,
|
||||
...others
|
||||
} = props;
|
||||
const { onClick } = useProps(props);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [formValueChanged, setFormValueChanged] = useState(false);
|
||||
const Designer = useDesigner();
|
||||
const field = useField<any>();
|
||||
const { run } = useAction();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const compile = useCompile();
|
||||
const form = useForm();
|
||||
const values = useRecord();
|
||||
const designerProps = fieldSchema['x-designer-props'];
|
||||
const openMode = fieldSchema?.['x-component-props']?.['openMode'];
|
||||
const disabled = form.disabled || field.disabled || props.disabled;
|
||||
const openSize = fieldSchema?.['x-component-props']?.['openSize'];
|
||||
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
|
||||
const { designable } = useDesignable();
|
||||
const tarComponent = useComponent(component) || component;
|
||||
useEffect(() => {
|
||||
field.linkageProperty = {};
|
||||
linkageRules
|
||||
.filter((k) => !k.disabled)
|
||||
.forEach((v) => {
|
||||
return v.actions?.map((h) => {
|
||||
linkageAction(h.operator, field, v.condition, values);
|
||||
});
|
||||
});
|
||||
}, [linkageRules, values, designable]);
|
||||
|
||||
const renderButton = () => {
|
||||
if (!designable && field?.data?.hidden) {
|
||||
export const Action: ComposedAction = observer(
|
||||
(props: any) => {
|
||||
const {
|
||||
popover,
|
||||
confirm,
|
||||
openMode: om,
|
||||
containerRefKey,
|
||||
component,
|
||||
useAction = useA,
|
||||
className,
|
||||
icon,
|
||||
title,
|
||||
...others
|
||||
} = props;
|
||||
const { onClick } = useProps(props);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [formValueChanged, setFormValueChanged] = useState(false);
|
||||
const Designer = useDesigner();
|
||||
const field = useField<any>();
|
||||
const { run } = useAction();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const compile = useCompile();
|
||||
const form = useForm();
|
||||
const values = useRecord();
|
||||
const designerProps = fieldSchema['x-designer-props'];
|
||||
const openMode = fieldSchema?.['x-component-props']?.['openMode'];
|
||||
const disabled = form.disabled || field.disabled || props.disabled;
|
||||
const openSize = fieldSchema?.['x-component-props']?.['openSize'];
|
||||
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
|
||||
const { designable } = useDesignable();
|
||||
const tarComponent = useComponent(component) || component;
|
||||
const renderTitle = useMemo(() => {
|
||||
if (title) {
|
||||
return title;
|
||||
}
|
||||
if (fieldSchema.title) {
|
||||
return compile(fieldSchema.title);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SortableItem
|
||||
{...others}
|
||||
loading={field?.data?.loading}
|
||||
icon={<Icon type={icon} />}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
opacity: designable && field?.data?.hidden && 0.1,
|
||||
}}
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
if (!disabled) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const onOk = () => {
|
||||
onClick?.(e);
|
||||
setVisible(true);
|
||||
run();
|
||||
};
|
||||
if (confirm) {
|
||||
Modal.confirm({
|
||||
...confirm,
|
||||
onOk,
|
||||
});
|
||||
} else {
|
||||
onOk();
|
||||
}, [compile, fieldSchema.title, title]);
|
||||
|
||||
useEffect(() => {
|
||||
field.linkageProperty = {};
|
||||
linkageRules
|
||||
.filter((k) => !k.disabled)
|
||||
.forEach((v) => {
|
||||
return v.actions?.map((h) => {
|
||||
linkageAction(h.operator, field, v.condition, values);
|
||||
});
|
||||
});
|
||||
}, [linkageRules, values, designable]);
|
||||
|
||||
const renderButton = () => {
|
||||
if (!designable && field?.data?.hidden) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SortableItem
|
||||
{...others}
|
||||
loading={field?.data?.loading}
|
||||
icon={<Icon type={icon} />}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
opacity: designable && field?.data?.hidden && 0.1,
|
||||
}}
|
||||
onClick={(e: React.MouseEvent) => {
|
||||
if (!disabled) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const onOk = () => {
|
||||
onClick?.(e);
|
||||
setVisible(true);
|
||||
run();
|
||||
};
|
||||
if (confirm) {
|
||||
Modal.confirm({
|
||||
...confirm,
|
||||
onOk,
|
||||
});
|
||||
} else {
|
||||
onOk();
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
component={tarComponent || Button}
|
||||
className={classnames(className, actionDesignerCss)}
|
||||
>
|
||||
{renderTitle}
|
||||
<Designer {...designerProps} />
|
||||
</SortableItem>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionContext.Provider
|
||||
value={{
|
||||
button: renderButton(),
|
||||
visible,
|
||||
setVisible,
|
||||
formValueChanged,
|
||||
setFormValueChanged,
|
||||
openMode,
|
||||
openSize,
|
||||
containerRefKey,
|
||||
fieldSchema,
|
||||
}}
|
||||
component={tarComponent || Button}
|
||||
className={classnames(className, actionDesignerCss)}
|
||||
>
|
||||
{title || compile(fieldSchema.title)}
|
||||
<Designer {...designerProps} />
|
||||
</SortableItem>
|
||||
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
|
||||
{!popover && renderButton()}
|
||||
{!popover && props.children}
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
};
|
||||
},
|
||||
{ displayName: 'Action' },
|
||||
);
|
||||
|
||||
return (
|
||||
<ActionContext.Provider
|
||||
value={{
|
||||
button: renderButton(),
|
||||
visible,
|
||||
setVisible,
|
||||
formValueChanged,
|
||||
setFormValueChanged,
|
||||
openMode,
|
||||
openSize,
|
||||
containerRefKey,
|
||||
fieldSchema,
|
||||
}}
|
||||
>
|
||||
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
|
||||
{!popover && renderButton()}
|
||||
{!popover && props.children}
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
});
|
||||
Action.Popover = observer(
|
||||
(props) => {
|
||||
const { button, visible, setVisible } = useActionContext();
|
||||
return (
|
||||
<Popover
|
||||
{...props}
|
||||
destroyTooltipOnHide
|
||||
open={visible}
|
||||
onOpenChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
content={props.children}
|
||||
>
|
||||
{button}
|
||||
</Popover>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Action.Popover' },
|
||||
);
|
||||
|
||||
Action.Popover = observer((props) => {
|
||||
const { button, visible, setVisible } = useActionContext();
|
||||
return (
|
||||
<Popover
|
||||
{...props}
|
||||
destroyTooltipOnHide
|
||||
open={visible}
|
||||
onOpenChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
content={props.children}
|
||||
>
|
||||
{button}
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
Action.Popover.Footer = observer((props) => {
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Action.Popover.Footer = observer(
|
||||
(props) => {
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Action.Popover.Footer' },
|
||||
);
|
||||
|
||||
Action.Link = ActionLink;
|
||||
Action.Designer = ActionDesigner;
|
||||
|
@ -6,40 +6,41 @@ import { useSchemaInitializer } from '../../../schema-initializer';
|
||||
import { DndContext } from '../../common';
|
||||
import { useDesignable, useProps } from '../../hooks';
|
||||
|
||||
export const ActionBar = observer((props: any) => {
|
||||
const { layout = 'tow-columns', style, spaceProps, ...others } = useProps(props);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
const { designable } = useDesignable();
|
||||
if (layout === 'one-column') {
|
||||
export const ActionBar = observer(
|
||||
(props: any) => {
|
||||
const { layout = 'tow-columns', style, spaceProps, ...others } = useProps(props);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
const { designable } = useDesignable();
|
||||
if (layout === 'one-column') {
|
||||
return (
|
||||
<DndContext>
|
||||
<div
|
||||
style={{ display: 'flex', alignItems: 'center', ...style }}
|
||||
{...others}
|
||||
className={cx(others.className, 'nb-action-bar')}
|
||||
>
|
||||
{props.children && (
|
||||
<div style={{ marginRight: 8 }}>
|
||||
<Space {...spaceProps}>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
return <RecursionField key={key} name={key} schema={schema} />;
|
||||
})}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
<InitializerComponent />
|
||||
</div>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
const hasActions = Object.keys(fieldSchema.properties ?? {}).length > 0;
|
||||
return (
|
||||
<DndContext>
|
||||
<div
|
||||
style={{ display: 'flex', alignItems: 'center', ...style }}
|
||||
{...others}
|
||||
className={cx(others.className, 'nb-action-bar')}
|
||||
>
|
||||
{props.children && (
|
||||
<div style={{ marginRight: 8 }}>
|
||||
<Space {...spaceProps}>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
return <RecursionField key={key} name={key} schema={schema} />;
|
||||
})}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
<InitializerComponent />
|
||||
</div>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
const hasActions = Object.keys(fieldSchema.properties ?? {}).length > 0;
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
!designable && !hasActions
|
||||
? undefined
|
||||
: {
|
||||
<div
|
||||
style={
|
||||
!designable && !hasActions
|
||||
? undefined
|
||||
: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
@ -47,38 +48,40 @@ export const ActionBar = observer((props: any) => {
|
||||
flexShrink: 0,
|
||||
...style,
|
||||
}
|
||||
}
|
||||
{...others}
|
||||
className={cx(others.className, 'nb-action-bar')}
|
||||
>
|
||||
<div
|
||||
className={css`
|
||||
.ant-space:last-child {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`}
|
||||
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}
|
||||
}
|
||||
{...others}
|
||||
className={cx(others.className, 'nb-action-bar')}
|
||||
>
|
||||
<DndContext>
|
||||
<Space {...spaceProps}>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
if (schema['x-align'] !== 'left') {
|
||||
return null;
|
||||
}
|
||||
return <RecursionField key={key} name={key} schema={schema} />;
|
||||
})}
|
||||
</Space>
|
||||
<Space {...spaceProps}>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
if (schema['x-align'] === 'left') {
|
||||
return null;
|
||||
}
|
||||
return <RecursionField key={key} name={key} schema={schema} />;
|
||||
})}
|
||||
</Space>
|
||||
</DndContext>
|
||||
<div
|
||||
className={css`
|
||||
.ant-space:last-child {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`}
|
||||
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}
|
||||
>
|
||||
<DndContext>
|
||||
<Space {...spaceProps}>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
if (schema['x-align'] !== 'left') {
|
||||
return null;
|
||||
}
|
||||
return <RecursionField key={key} name={key} schema={schema} />;
|
||||
})}
|
||||
</Space>
|
||||
<Space {...spaceProps}>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
if (schema['x-align'] === 'left') {
|
||||
return null;
|
||||
}
|
||||
return <RecursionField key={key} name={key} schema={schema} />;
|
||||
})}
|
||||
</Space>
|
||||
</DndContext>
|
||||
</div>
|
||||
<InitializerComponent />
|
||||
</div>
|
||||
<InitializerComponent />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
{ displayName: 'ActionBar' },
|
||||
);
|
||||
|
@ -18,30 +18,33 @@ const useCloseAction = () => {
|
||||
};
|
||||
};
|
||||
|
||||
const Editable = observer((props) => {
|
||||
const field = useField<Field>();
|
||||
const schema = useFieldSchema();
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
打开方式:
|
||||
<Select
|
||||
value={field.componentProps.openMode}
|
||||
style={{ width: 100 }}
|
||||
onChange={(value) => {
|
||||
field.componentProps.openMode = value;
|
||||
schema['x-component-props']['openMode'] = value;
|
||||
}}
|
||||
>
|
||||
<Select.Option value={'drawer'}>Drawer</Select.Option>
|
||||
<Select.Option value={'modal'}>Modal</Select.Option>
|
||||
<Select.Option value={'page'}>Page</Select.Option>
|
||||
</Select>
|
||||
const Editable = observer(
|
||||
(props) => {
|
||||
const field = useField<Field>();
|
||||
const schema = useFieldSchema();
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
打开方式:
|
||||
<Select
|
||||
value={field.componentProps.openMode}
|
||||
style={{ width: 100 }}
|
||||
onChange={(value) => {
|
||||
field.componentProps.openMode = value;
|
||||
schema['x-component-props']['openMode'] = value;
|
||||
}}
|
||||
>
|
||||
<Select.Option value={'drawer'}>Drawer</Select.Option>
|
||||
<Select.Option value={'modal'}>Modal</Select.Option>
|
||||
<Select.Option value={'page'}>Page</Select.Option>
|
||||
</Select>
|
||||
</div>
|
||||
{props.children}
|
||||
</div>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
{ displayName: 'Editable' },
|
||||
);
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
|
@ -71,52 +71,55 @@ function TypeSelect(props) {
|
||||
);
|
||||
}
|
||||
|
||||
const EditOptions = observer((props) => {
|
||||
const record = ArrayTable.useRecord();
|
||||
const field = useField<Field>();
|
||||
console.log(record.type);
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
onClick={() => {
|
||||
FormDrawer('Pop-up form', () => {
|
||||
return (
|
||||
<FormLayout labelCol={6} wrapperCol={10}>
|
||||
<SchemaField
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: record.type ? types[record.type] || {} : {},
|
||||
}}
|
||||
/>
|
||||
<FormDrawer.Footer>
|
||||
<FormButtonGroup align="right">
|
||||
<Submit
|
||||
onSubmit={() => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Submit>
|
||||
</FormButtonGroup>
|
||||
</FormDrawer.Footer>
|
||||
</FormLayout>
|
||||
);
|
||||
})
|
||||
.open({
|
||||
initialValues: field.value,
|
||||
const EditOptions = observer(
|
||||
(props) => {
|
||||
const record = ArrayTable.useRecord();
|
||||
const field = useField<Field>();
|
||||
console.log(record.type);
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
onClick={() => {
|
||||
FormDrawer('Pop-up form', () => {
|
||||
return (
|
||||
<FormLayout labelCol={6} wrapperCol={10}>
|
||||
<SchemaField
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: record.type ? types[record.type] || {} : {},
|
||||
}}
|
||||
/>
|
||||
<FormDrawer.Footer>
|
||||
<FormButtonGroup align="right">
|
||||
<Submit
|
||||
onSubmit={() => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Submit>
|
||||
</FormButtonGroup>
|
||||
</FormDrawer.Footer>
|
||||
</FormLayout>
|
||||
);
|
||||
})
|
||||
.then((values) => {
|
||||
field.value = values;
|
||||
});
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
.open({
|
||||
initialValues: field.value,
|
||||
})
|
||||
.then((values) => {
|
||||
field.value = values;
|
||||
});
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'EditOptions' },
|
||||
);
|
||||
|
||||
const SchemaField = createSchemaField({
|
||||
components: {
|
||||
|
@ -4,63 +4,66 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useCollectionManager } from '../../../collection-manager';
|
||||
import { AssociationFieldContext } from './context';
|
||||
|
||||
export const AssociationFieldProvider = observer((props) => {
|
||||
const field = useField<Field>();
|
||||
const { getCollectionJoinField, getCollection } = useCollectionManager();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const allowMultiple = fieldSchema['x-component-props']?.multiple !== false;
|
||||
const allowDissociate = fieldSchema['x-component-props']?.allowDissociate !== false;
|
||||
export const AssociationFieldProvider = observer(
|
||||
(props) => {
|
||||
const field = useField<Field>();
|
||||
const { getCollectionJoinField, getCollection } = useCollectionManager();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const allowMultiple = fieldSchema['x-component-props']?.multiple !== false;
|
||||
const allowDissociate = fieldSchema['x-component-props']?.allowDissociate !== false;
|
||||
|
||||
const collectionField = useMemo(
|
||||
() => getCollectionJoinField(fieldSchema['x-collection-field']),
|
||||
[fieldSchema['x-collection-field'], fieldSchema.name],
|
||||
);
|
||||
const isFileCollection = useMemo(
|
||||
() => getCollection(collectionField?.target)?.template === 'file',
|
||||
[fieldSchema['x-collection-field']],
|
||||
);
|
||||
const currentMode = useMemo(
|
||||
() => fieldSchema['x-component-props']?.mode || (isFileCollection ? 'FileManager' : 'Select'),
|
||||
[fieldSchema['x-component-props']?.mode],
|
||||
);
|
||||
const collectionField = useMemo(
|
||||
() => getCollectionJoinField(fieldSchema['x-collection-field']),
|
||||
[fieldSchema['x-collection-field'], fieldSchema.name],
|
||||
);
|
||||
const isFileCollection = useMemo(
|
||||
() => getCollection(collectionField?.target)?.template === 'file',
|
||||
[fieldSchema['x-collection-field']],
|
||||
);
|
||||
const currentMode = useMemo(
|
||||
() => fieldSchema['x-component-props']?.mode || (isFileCollection ? 'FileManager' : 'Select'),
|
||||
[fieldSchema['x-component-props']?.mode],
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
if (!collectionField) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (field.value !== null && field.value !== undefined) {
|
||||
// Nester 子表单时,如果没数据初始化一个 [null] 的占位
|
||||
if (currentMode === 'Nester' && Array.isArray(field.value)) {
|
||||
if (field.value.length === 0 && ['belongsToMany', 'hasMany'].includes(collectionField.type)) {
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
if (!collectionField) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (field.value !== null && field.value !== undefined) {
|
||||
// Nester 子表单时,如果没数据初始化一个 [null] 的占位
|
||||
if (currentMode === 'Nester' && Array.isArray(field.value)) {
|
||||
if (field.value.length === 0 && ['belongsToMany', 'hasMany'].includes(collectionField.type)) {
|
||||
field.value = [{}];
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (currentMode === 'Nester') {
|
||||
if (['belongsTo', 'hasOne'].includes(collectionField.type)) {
|
||||
field.value = {};
|
||||
} else if (['belongsToMany', 'hasMany'].includes(collectionField.type)) {
|
||||
field.value = [{}];
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (currentMode === 'Nester') {
|
||||
if (['belongsTo', 'hasOne'].includes(collectionField.type)) {
|
||||
field.value = {};
|
||||
} else if (['belongsToMany', 'hasMany'].includes(collectionField.type)) {
|
||||
field.value = [{}];
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
}, [currentMode, collectionField, field.value]);
|
||||
}, [currentMode, collectionField, field.value]);
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return collectionField ? (
|
||||
<AssociationFieldContext.Provider
|
||||
value={{ options: collectionField, field, allowMultiple, allowDissociate, currentMode }}
|
||||
>
|
||||
{props.children}
|
||||
</AssociationFieldContext.Provider>
|
||||
) : null;
|
||||
});
|
||||
return collectionField ? (
|
||||
<AssociationFieldContext.Provider
|
||||
value={{ options: collectionField, field, allowMultiple, allowDissociate, currentMode }}
|
||||
>
|
||||
{props.children}
|
||||
</AssociationFieldContext.Provider>
|
||||
) : null;
|
||||
},
|
||||
{ displayName: 'AssociationFieldProvider' },
|
||||
);
|
||||
|
@ -11,41 +11,44 @@ export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & {
|
||||
multiple?: boolean;
|
||||
};
|
||||
|
||||
const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
|
||||
const { fieldNames, objectValue = true } = props;
|
||||
const field: any = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const service = useServiceOptions(props);
|
||||
const isAllowAddNew = fieldSchema['x-add-new'];
|
||||
const value = Array.isArray(props.value) ? props.value.filter(Boolean) : props.value;
|
||||
const InternalAssociationSelect = observer(
|
||||
(props: AssociationSelectProps) => {
|
||||
const { fieldNames, objectValue = true } = props;
|
||||
const field: any = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const service = useServiceOptions(props);
|
||||
const isAllowAddNew = fieldSchema['x-add-new'];
|
||||
const value = Array.isArray(props.value) ? props.value.filter(Boolean) : props.value;
|
||||
|
||||
return (
|
||||
<div key={fieldSchema.name}>
|
||||
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
||||
<RemoteSelect
|
||||
style={{ width: '100%' }}
|
||||
{...props}
|
||||
objectValue={objectValue}
|
||||
value={value}
|
||||
service={service}
|
||||
></RemoteSelect>
|
||||
return (
|
||||
<div key={fieldSchema.name}>
|
||||
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
||||
<RemoteSelect
|
||||
style={{ width: '100%' }}
|
||||
{...props}
|
||||
objectValue={objectValue}
|
||||
value={value}
|
||||
service={service}
|
||||
></RemoteSelect>
|
||||
|
||||
{isAllowAddNew && (
|
||||
<RecordProvider record={null}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'Action';
|
||||
}}
|
||||
/>
|
||||
</RecordProvider>
|
||||
)}
|
||||
</Input.Group>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
{isAllowAddNew && (
|
||||
<RecordProvider record={null}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'Action';
|
||||
}}
|
||||
/>
|
||||
</RecordProvider>
|
||||
)}
|
||||
</Input.Group>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'InternalAssociationSelect' },
|
||||
);
|
||||
|
||||
interface AssociationSelectInterface {
|
||||
(props: any): React.ReactElement;
|
||||
|
@ -12,50 +12,56 @@ import { InternalSubTable } from './InternalSubTable';
|
||||
import { CreateRecordAction } from './components/CreateRecordAction';
|
||||
import { useAssociationFieldContext } from './hooks';
|
||||
|
||||
const EditableAssociationField = observer((props: any) => {
|
||||
const { multiple } = props;
|
||||
const field: Field = useField();
|
||||
const form = useForm();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { options: collectionField, currentMode } = useAssociationFieldContext();
|
||||
const EditableAssociationField = observer(
|
||||
(props: any) => {
|
||||
const { multiple } = props;
|
||||
const field: Field = useField();
|
||||
const form = useForm();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { options: collectionField, currentMode } = useAssociationFieldContext();
|
||||
|
||||
const useCreateActionProps = () => {
|
||||
const { onClick } = useCAP();
|
||||
const actionField: any = useField();
|
||||
return {
|
||||
async onClick() {
|
||||
await onClick();
|
||||
const { data } = actionField.data?.data?.data || {};
|
||||
if (data) {
|
||||
if (['m2m', 'o2m'].includes(collectionField?.interface) && multiple !== false) {
|
||||
const values = form.getValuesIn(field.path) || [];
|
||||
values.push(data);
|
||||
form.setValuesIn(field.path, values);
|
||||
field.onInput(values);
|
||||
} else {
|
||||
form.setValuesIn(field.path, data);
|
||||
field.onInput(data);
|
||||
const useCreateActionProps = () => {
|
||||
const { onClick } = useCAP();
|
||||
const actionField: any = useField();
|
||||
return {
|
||||
async onClick() {
|
||||
await onClick();
|
||||
const { data } = actionField.data?.data?.data || {};
|
||||
if (data) {
|
||||
if (['m2m', 'o2m'].includes(collectionField?.interface) && multiple !== false) {
|
||||
const values = form.getValuesIn(field.path) || [];
|
||||
values.push(data);
|
||||
form.setValuesIn(field.path, values);
|
||||
field.onInput(values);
|
||||
} else {
|
||||
form.setValuesIn(field.path, data);
|
||||
field.onInput(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<SchemaComponentOptions scope={{ useCreateActionProps }} components={{ CreateRecordAction }}>
|
||||
{currentMode === 'Picker' && <InternalPicker {...props} />}
|
||||
{currentMode === 'Nester' && <InternalNester {...props} />}
|
||||
{currentMode === 'Select' && <AssociationSelect {...props} />}
|
||||
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
||||
{currentMode === 'FileManager' && <InternalFileManager {...props} />}
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<SchemaComponentOptions scope={{ useCreateActionProps }} components={{ CreateRecordAction }}>
|
||||
{currentMode === 'Picker' && <InternalPicker {...props} />}
|
||||
{currentMode === 'Nester' && <InternalNester {...props} />}
|
||||
{currentMode === 'Select' && <AssociationSelect {...props} />}
|
||||
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
||||
{currentMode === 'FileManager' && <InternalFileManager {...props} />}
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
},
|
||||
{ displayName: 'EditableAssociationField' },
|
||||
);
|
||||
|
||||
export const Editable = observer((props) => {
|
||||
return (
|
||||
<AssociationFieldProvider>
|
||||
<EditableAssociationField {...props} />
|
||||
</AssociationFieldProvider>
|
||||
);
|
||||
});
|
||||
export const Editable = observer(
|
||||
(props) => {
|
||||
return (
|
||||
<AssociationFieldProvider>
|
||||
<EditableAssociationField {...props} />
|
||||
</AssociationFieldProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Editable' },
|
||||
);
|
||||
|
@ -60,138 +60,145 @@ const useTableSelectorProps = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const InternalPicker = observer((props: any) => {
|
||||
const { value, multiple, onChange, quickUpload, selectFile, ...others } = props;
|
||||
const field: any = useField();
|
||||
const fieldNames = useFieldNames(props);
|
||||
const [visibleSelector, setVisibleSelector] = useState(false);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const insertSelector = useInsertSchema('Selector');
|
||||
const { options: collectionField } = useAssociationFieldContext();
|
||||
const compile = useCompile();
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const isAllowAddNew = fieldSchema['x-add-new'];
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const options = useMemo(() => {
|
||||
if (value && Object.keys(value).length > 0) {
|
||||
const opts = (Array.isArray(value) ? value : value ? [value] : []).filter(Boolean).map((option) => {
|
||||
const label = option?.[fieldNames.label];
|
||||
return {
|
||||
...option,
|
||||
[fieldNames.label]: getLabelFormatValue(compile(labelUiSchema), compile(label)),
|
||||
};
|
||||
});
|
||||
return opts;
|
||||
}
|
||||
return [];
|
||||
}, [value, fieldNames?.label]);
|
||||
export const InternalPicker = observer(
|
||||
(props: any) => {
|
||||
const { value, multiple, onChange, quickUpload, selectFile, ...others } = props;
|
||||
const field: any = useField();
|
||||
const fieldNames = useFieldNames(props);
|
||||
const [visibleSelector, setVisibleSelector] = useState(false);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const insertSelector = useInsertSchema('Selector');
|
||||
const { options: collectionField } = useAssociationFieldContext();
|
||||
const compile = useCompile();
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const isAllowAddNew = fieldSchema['x-add-new'];
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const options = useMemo(() => {
|
||||
if (value && Object.keys(value).length > 0) {
|
||||
const opts = (Array.isArray(value) ? value : value ? [value] : []).filter(Boolean).map((option) => {
|
||||
const label = option?.[fieldNames.label];
|
||||
return {
|
||||
...option,
|
||||
[fieldNames.label]: getLabelFormatValue(compile(labelUiSchema), compile(label)),
|
||||
};
|
||||
});
|
||||
return opts;
|
||||
}
|
||||
return [];
|
||||
}, [value, fieldNames?.label]);
|
||||
|
||||
const pickerProps = {
|
||||
size: 'small',
|
||||
fieldNames,
|
||||
multiple: multiple !== false && ['o2m', 'm2m'].includes(collectionField?.interface),
|
||||
association: {
|
||||
target: collectionField?.target,
|
||||
},
|
||||
options,
|
||||
onChange: props?.onChange,
|
||||
selectedRows,
|
||||
setSelectedRows,
|
||||
collectionField,
|
||||
};
|
||||
|
||||
const getValue = () => {
|
||||
if (multiple == null) return null;
|
||||
return Array.isArray(value) ? value.filter(Boolean)?.map((v) => v?.[fieldNames.value]) : value?.[fieldNames.value];
|
||||
};
|
||||
const getFilter = () => {
|
||||
const targetKey = collectionField?.targetKey || 'id';
|
||||
const list = options.map((option) => option[targetKey]).filter(Boolean);
|
||||
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
|
||||
return filter;
|
||||
};
|
||||
const usePickActionProps = () => {
|
||||
const { setVisible } = useActionContext();
|
||||
const { multiple, selectedRows, onChange, options, collectionField } = useContext(RecordPickerContext);
|
||||
return {
|
||||
onClick() {
|
||||
if (multiple) {
|
||||
onChange(unionBy(selectedRows, options, collectionField?.targetKey || 'id'));
|
||||
} else {
|
||||
onChange(selectedRows?.[0] || null);
|
||||
}
|
||||
setVisible(false);
|
||||
const pickerProps = {
|
||||
size: 'small',
|
||||
fieldNames,
|
||||
multiple: multiple !== false && ['o2m', 'm2m'].includes(collectionField?.interface),
|
||||
association: {
|
||||
target: collectionField?.target,
|
||||
},
|
||||
options,
|
||||
onChange: props?.onChange,
|
||||
selectedRows,
|
||||
setSelectedRows,
|
||||
collectionField,
|
||||
};
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
{...others}
|
||||
mode={multiple ? 'multiple' : props.mode}
|
||||
fieldNames={fieldNames}
|
||||
onDropdownVisibleChange={(open) => {
|
||||
insertSelector(schema.Selector);
|
||||
setVisibleSelector(true);
|
||||
}}
|
||||
allowClear
|
||||
onChange={(changed: any) => {
|
||||
if (!changed) {
|
||||
const value = multiple ? [] : null;
|
||||
onChange(value);
|
||||
setSelectedRows(value);
|
||||
} else if (Array.isArray(changed)) {
|
||||
if (!changed.length) {
|
||||
onChange([]);
|
||||
setSelectedRows([]);
|
||||
return;
|
||||
}
|
||||
const values = options?.filter((option) => changed.includes(option[fieldNames.value]));
|
||||
onChange(values);
|
||||
setSelectedRows(values);
|
||||
}
|
||||
}}
|
||||
options={options}
|
||||
value={getValue()}
|
||||
open={false}
|
||||
/>
|
||||
</div>
|
||||
{isAllowAddNew && (
|
||||
<RecordProvider record={null}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'Action';
|
||||
|
||||
const getValue = () => {
|
||||
if (multiple == null) return null;
|
||||
return Array.isArray(value)
|
||||
? value.filter(Boolean)?.map((v) => v?.[fieldNames.value])
|
||||
: value?.[fieldNames.value];
|
||||
};
|
||||
const getFilter = () => {
|
||||
const targetKey = collectionField?.targetKey || 'id';
|
||||
const list = options.map((option) => option[targetKey]).filter(Boolean);
|
||||
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
|
||||
return filter;
|
||||
};
|
||||
const usePickActionProps = () => {
|
||||
const { setVisible } = useActionContext();
|
||||
const { multiple, selectedRows, onChange, options, collectionField } = useContext(RecordPickerContext);
|
||||
return {
|
||||
onClick() {
|
||||
if (multiple) {
|
||||
onChange(unionBy(selectedRows, options, collectionField?.targetKey || 'id'));
|
||||
} else {
|
||||
onChange(selectedRows?.[0] || null);
|
||||
}
|
||||
setVisible(false);
|
||||
},
|
||||
};
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
{...others}
|
||||
mode={multiple ? 'multiple' : props.mode}
|
||||
fieldNames={fieldNames}
|
||||
onDropdownVisibleChange={(open) => {
|
||||
insertSelector(schema.Selector);
|
||||
setVisibleSelector(true);
|
||||
}}
|
||||
allowClear
|
||||
onChange={(changed: any) => {
|
||||
if (!changed) {
|
||||
const value = multiple ? [] : null;
|
||||
onChange(value);
|
||||
setSelectedRows(value);
|
||||
} else if (Array.isArray(changed)) {
|
||||
if (!changed.length) {
|
||||
onChange([]);
|
||||
setSelectedRows([]);
|
||||
return;
|
||||
}
|
||||
const values = options?.filter((option) => changed.includes(option[fieldNames.value]));
|
||||
onChange(values);
|
||||
setSelectedRows(values);
|
||||
}
|
||||
}}
|
||||
options={options}
|
||||
value={getValue()}
|
||||
open={false}
|
||||
/>
|
||||
</RecordProvider>
|
||||
)}
|
||||
</Input.Group>
|
||||
<ActionContext.Provider value={{ openMode: 'drawer', visible: visibleSelector, setVisible: setVisibleSelector }}>
|
||||
<RecordPickerProvider {...pickerProps}>
|
||||
<CollectionProvider name={collectionField?.target}>
|
||||
<FormProvider>
|
||||
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
|
||||
<SchemaComponentOptions scope={{ usePickActionProps, useTableSelectorProps }}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'AssociationField.Selector';
|
||||
}}
|
||||
/>
|
||||
</SchemaComponentOptions>
|
||||
</TableSelectorParamsProvider>
|
||||
</FormProvider>
|
||||
</CollectionProvider>
|
||||
</RecordPickerProvider>
|
||||
</ActionContext.Provider>
|
||||
</>
|
||||
);
|
||||
});
|
||||
</div>
|
||||
{isAllowAddNew && (
|
||||
<RecordProvider record={null}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'Action';
|
||||
}}
|
||||
/>
|
||||
</RecordProvider>
|
||||
)}
|
||||
</Input.Group>
|
||||
<ActionContext.Provider
|
||||
value={{ openMode: 'drawer', visible: visibleSelector, setVisible: setVisibleSelector }}
|
||||
>
|
||||
<RecordPickerProvider {...pickerProps}>
|
||||
<CollectionProvider name={collectionField?.target}>
|
||||
<FormProvider>
|
||||
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
|
||||
<SchemaComponentOptions scope={{ usePickActionProps, useTableSelectorProps }}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'AssociationField.Selector';
|
||||
}}
|
||||
/>
|
||||
</SchemaComponentOptions>
|
||||
</TableSelectorParamsProvider>
|
||||
</FormProvider>
|
||||
</CollectionProvider>
|
||||
</RecordPickerProvider>
|
||||
</ActionContext.Provider>
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'InternalPicker' },
|
||||
);
|
||||
|
@ -22,94 +22,97 @@ const toValue = (value, placeholder) => {
|
||||
}
|
||||
return value;
|
||||
};
|
||||
export const ReadPrettyInternalViewer: React.FC = observer((props: any) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const recordCtx = useRecord();
|
||||
const { enableLink } = fieldSchema['x-component-props'];
|
||||
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
||||
const field = useField();
|
||||
const fieldNames = useFieldNames(props);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const insertViewer = useInsertSchema('Viewer');
|
||||
const { options: collectionField } = useAssociationFieldContext();
|
||||
const [record, setRecord] = useState({});
|
||||
const compile = useCompile();
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const { snapshot } = useActionContext();
|
||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||
export const ReadPrettyInternalViewer: React.FC = observer(
|
||||
(props: any) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const recordCtx = useRecord();
|
||||
const { enableLink } = fieldSchema['x-component-props'];
|
||||
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
||||
const field = useField();
|
||||
const fieldNames = useFieldNames(props);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const insertViewer = useInsertSchema('Viewer');
|
||||
const { options: collectionField } = useAssociationFieldContext();
|
||||
const [record, setRecord] = useState({});
|
||||
const compile = useCompile();
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const { snapshot } = useActionContext();
|
||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||
|
||||
const renderRecords = () =>
|
||||
toArr(props.value).map((record, index, arr) => {
|
||||
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
||||
const text = getLabelFormatValue(compile(labelUiSchema), val, true);
|
||||
return (
|
||||
<Fragment key={`${record.id}_${index}`}>
|
||||
<span>
|
||||
{snapshot ? (
|
||||
text
|
||||
) : enableLink !== false ? (
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
insertViewer(schema.Viewer);
|
||||
setVisible(true);
|
||||
setRecord(record);
|
||||
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
) : (
|
||||
text
|
||||
)}
|
||||
</span>
|
||||
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
const renderRecords = () =>
|
||||
toArr(props.value).map((record, index, arr) => {
|
||||
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
||||
const text = getLabelFormatValue(compile(labelUiSchema), val, true);
|
||||
return (
|
||||
<Fragment key={`${record.id}_${index}`}>
|
||||
<span>
|
||||
{snapshot ? (
|
||||
text
|
||||
) : enableLink !== false ? (
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
insertViewer(schema.Viewer);
|
||||
setVisible(true);
|
||||
setRecord(record);
|
||||
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
) : (
|
||||
text
|
||||
)}
|
||||
</span>
|
||||
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
const renderWithoutTableFieldResourceProvider = () => (
|
||||
<WithoutTableFieldResource.Provider value={true}>
|
||||
<FormProvider>
|
||||
<RecursionField
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'AssociationField.Viewer';
|
||||
}}
|
||||
/>
|
||||
</FormProvider>
|
||||
</WithoutTableFieldResource.Provider>
|
||||
);
|
||||
|
||||
const renderRecordProvider = () => {
|
||||
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||
|
||||
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
</RecordProvider>
|
||||
) : (
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
const renderWithoutTableFieldResourceProvider = () => (
|
||||
<WithoutTableFieldResource.Provider value={true}>
|
||||
<FormProvider>
|
||||
<RecursionField
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'AssociationField.Viewer';
|
||||
}}
|
||||
/>
|
||||
</FormProvider>
|
||||
</WithoutTableFieldResource.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BlockAssociationContext.Provider value={`${collectionField?.collectionName}.${collectionField?.name}`}>
|
||||
<CollectionProvider name={collectionField?.target ?? collectionField?.targetCollection}>
|
||||
<EllipsisWithTooltip ellipsis={true} ref={ellipsisWithTooltipRef}>
|
||||
{renderRecords()}
|
||||
</EllipsisWithTooltip>
|
||||
<ActionContext.Provider
|
||||
value={{ visible, setVisible, openMode: 'drawer', snapshot: collectionField?.interface === 'snapshot' }}
|
||||
>
|
||||
{renderRecordProvider()}
|
||||
</ActionContext.Provider>
|
||||
</CollectionProvider>
|
||||
</BlockAssociationContext.Provider>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const renderRecordProvider = () => {
|
||||
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||
|
||||
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
</RecordProvider>
|
||||
) : (
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BlockAssociationContext.Provider value={`${collectionField?.collectionName}.${collectionField?.name}`}>
|
||||
<CollectionProvider name={collectionField?.target ?? collectionField?.targetCollection}>
|
||||
<EllipsisWithTooltip ellipsis={true} ref={ellipsisWithTooltipRef}>
|
||||
{renderRecords()}
|
||||
</EllipsisWithTooltip>
|
||||
<ActionContext.Provider
|
||||
value={{ visible, setVisible, openMode: 'drawer', snapshot: collectionField?.interface === 'snapshot' }}
|
||||
>
|
||||
{renderRecordProvider()}
|
||||
</ActionContext.Provider>
|
||||
</CollectionProvider>
|
||||
</BlockAssociationContext.Provider>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ReadPrettyInternalViewer' },
|
||||
);
|
||||
|
@ -25,92 +25,95 @@ const ToOneNester = (props) => {
|
||||
return <Card bordered={true}>{props.children}</Card>;
|
||||
};
|
||||
|
||||
const ToManyNester = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext<ArrayField>();
|
||||
const { t } = useTranslation();
|
||||
return (field.value || []).length > 0 ? (
|
||||
<Card
|
||||
bordered={true}
|
||||
style={{ position: 'relative' }}
|
||||
className={css`
|
||||
> .ant-card-body > .ant-divider:last-child {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
>
|
||||
{(field.value || []).map((value, index) => {
|
||||
let allowed = allowDissociate;
|
||||
if (!allowDissociate) {
|
||||
allowed = !value?.[options.targetKey];
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{field.editable && allowMultiple && (
|
||||
<Tooltip key={'add'} title={t('Add new')}>
|
||||
<PlusOutlined
|
||||
style={{ zIndex: 1000, marginRight: '10px', color: '#a8a3a3' }}
|
||||
onClick={() => {
|
||||
action(() => {
|
||||
if (!Array.isArray(field.value)) {
|
||||
field.value = [];
|
||||
}
|
||||
spliceArrayState(field as any, {
|
||||
startIndex: index + 1,
|
||||
insertCount: 1,
|
||||
const ToManyNester = observer(
|
||||
(props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext<ArrayField>();
|
||||
const { t } = useTranslation();
|
||||
return (field.value || []).length > 0 ? (
|
||||
<Card
|
||||
bordered={true}
|
||||
style={{ position: 'relative' }}
|
||||
className={css`
|
||||
> .ant-card-body > .ant-divider:last-child {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
>
|
||||
{(field.value || []).map((value, index) => {
|
||||
let allowed = allowDissociate;
|
||||
if (!allowDissociate) {
|
||||
allowed = !value?.[options.targetKey];
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{field.editable && allowMultiple && (
|
||||
<Tooltip key={'add'} title={t('Add new')}>
|
||||
<PlusOutlined
|
||||
style={{ zIndex: 1000, marginRight: '10px', color: '#a8a3a3' }}
|
||||
onClick={() => {
|
||||
action(() => {
|
||||
if (!Array.isArray(field.value)) {
|
||||
field.value = [];
|
||||
}
|
||||
spliceArrayState(field as any, {
|
||||
startIndex: index + 1,
|
||||
insertCount: 1,
|
||||
});
|
||||
field.value.splice(index + 1, 0, {});
|
||||
return field.onInput(field.value);
|
||||
});
|
||||
field.value.splice(index + 1, 0, {});
|
||||
return field.onInput(field.value);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!field.readPretty && allowed && (
|
||||
<Tooltip key={'remove'} title={t('Remove')}>
|
||||
<CloseOutlined
|
||||
style={{ zIndex: 1000, color: '#a8a3a3' }}
|
||||
onClick={() => {
|
||||
action(() => {
|
||||
spliceArrayState(field as any, {
|
||||
startIndex: index,
|
||||
deleteCount: 1,
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!field.readPretty && allowed && (
|
||||
<Tooltip key={'remove'} title={t('Remove')}>
|
||||
<CloseOutlined
|
||||
style={{ zIndex: 1000, color: '#a8a3a3' }}
|
||||
onClick={() => {
|
||||
action(() => {
|
||||
spliceArrayState(field as any, {
|
||||
startIndex: index,
|
||||
deleteCount: 1,
|
||||
});
|
||||
field.value.splice(index, 1);
|
||||
return field.onInput(field.value);
|
||||
});
|
||||
field.value.splice(index, 1);
|
||||
return field.onInput(field.value);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<RecursionField onlyRenderProperties basePath={field.address.concat(index)} schema={fieldSchema} />
|
||||
<Divider />
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</Card>
|
||||
) : (
|
||||
<>
|
||||
{field.editable && allowMultiple && (
|
||||
<Tooltip key={'add'} title={t('Add new')}>
|
||||
<Button
|
||||
type={'default'}
|
||||
className={css`
|
||||
border: 1px solid #f0f0f0 !important;
|
||||
box-shadow: none;
|
||||
`}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
const result = field.value;
|
||||
result.push({});
|
||||
field.value = result;
|
||||
}}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<RecursionField onlyRenderProperties basePath={field.address.concat(index)} schema={fieldSchema} />
|
||||
<Divider />
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</Card>
|
||||
) : (
|
||||
<>
|
||||
{field.editable && allowMultiple && (
|
||||
<Tooltip key={'add'} title={t('Add new')}>
|
||||
<Button
|
||||
type={'default'}
|
||||
className={css`
|
||||
border: 1px solid #f0f0f0 !important;
|
||||
box-shadow: none;
|
||||
`}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
const result = field.value;
|
||||
result.push({});
|
||||
field.value = result;
|
||||
}}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ToManyNester' },
|
||||
);
|
||||
|
@ -7,23 +7,29 @@ import { InternalSubTable } from './InternalSubTable';
|
||||
import { FileManageReadPretty } from './FileManager';
|
||||
import { useAssociationFieldContext } from './hooks';
|
||||
|
||||
const ReadPrettyAssociationField = observer((props: any) => {
|
||||
const { currentMode } = useAssociationFieldContext();
|
||||
const ReadPrettyAssociationField = observer(
|
||||
(props: any) => {
|
||||
const { currentMode } = useAssociationFieldContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
{['Select', 'Picker'].includes(currentMode) && <ReadPrettyInternalViewer {...props} />}
|
||||
{currentMode === 'Nester' && <InternalNester {...props} />}
|
||||
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
||||
{currentMode === 'FileManager' && <FileManageReadPretty {...props} />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{['Select', 'Picker'].includes(currentMode) && <ReadPrettyInternalViewer {...props} />}
|
||||
{currentMode === 'Nester' && <InternalNester {...props} />}
|
||||
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
||||
{currentMode === 'FileManager' && <FileManageReadPretty {...props} />}
|
||||
</>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ReadPrettyAssociationField' },
|
||||
);
|
||||
|
||||
export const ReadPretty = observer((props) => {
|
||||
return (
|
||||
<AssociationFieldProvider>
|
||||
<ReadPrettyAssociationField {...props} />
|
||||
</AssociationFieldProvider>
|
||||
);
|
||||
});
|
||||
export const ReadPretty = observer(
|
||||
(props) => {
|
||||
return (
|
||||
<AssociationFieldProvider>
|
||||
<ReadPrettyAssociationField {...props} />
|
||||
</AssociationFieldProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ReadPretty' },
|
||||
);
|
||||
|
@ -5,24 +5,27 @@ import React from 'react';
|
||||
import { Table } from '../table-v2/Table';
|
||||
import { useAssociationFieldContext } from './hooks';
|
||||
|
||||
export const SubTable: any = observer((props) => {
|
||||
const { field } = useAssociationFieldContext<ArrayField>();
|
||||
return (
|
||||
<div>
|
||||
<Table size={'small'} showIndex field={field} pagination={false} />
|
||||
{field.editable && (
|
||||
<Button
|
||||
type={'dashed'}
|
||||
block
|
||||
style={{ marginTop: 12 }}
|
||||
onClick={() => {
|
||||
field.value = field.value || [];
|
||||
field.value.push({});
|
||||
}}
|
||||
>
|
||||
Add new
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const SubTable: any = observer(
|
||||
(props) => {
|
||||
const { field } = useAssociationFieldContext<ArrayField>();
|
||||
return (
|
||||
<div>
|
||||
<Table size={'small'} showIndex field={field} pagination={false} />
|
||||
{field.editable && (
|
||||
<Button
|
||||
type={'dashed'}
|
||||
block
|
||||
style={{ marginTop: 12 }}
|
||||
onClick={() => {
|
||||
field.value = field.value || [];
|
||||
field.value.push({});
|
||||
}}
|
||||
>
|
||||
Add new
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'SubTable' },
|
||||
);
|
||||
|
@ -6,34 +6,37 @@ import { ActionContext, useActionContext } from '../../action';
|
||||
import { useAssociationFieldContext, useInsertSchema } from '../hooks';
|
||||
import schema from '../schema';
|
||||
|
||||
export const CreateRecordAction = observer((props) => {
|
||||
const field: any = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const ctx = useActionContext();
|
||||
const insertAddNewer = useInsertSchema('AddNewer');
|
||||
const { options: collectionField } = useAssociationFieldContext();
|
||||
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
|
||||
const [currentCollection, setCurrentCollection] = useState(collectionField?.target);
|
||||
const addbuttonClick = (name) => {
|
||||
insertAddNewer(schema.AddNewer);
|
||||
setVisibleAddNewer(true);
|
||||
setCurrentCollection(name);
|
||||
};
|
||||
return (
|
||||
<CollectionProvider name={collectionField?.target}>
|
||||
<CreateAction {...props} onClick={(arg) => addbuttonClick(arg)} />
|
||||
<ActionContext.Provider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
|
||||
<CollectionProvider name={currentCollection}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'AssociationField.AddNewer';
|
||||
}}
|
||||
/>
|
||||
</CollectionProvider>
|
||||
</ActionContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
});
|
||||
export const CreateRecordAction = observer(
|
||||
(props) => {
|
||||
const field: any = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const ctx = useActionContext();
|
||||
const insertAddNewer = useInsertSchema('AddNewer');
|
||||
const { options: collectionField } = useAssociationFieldContext();
|
||||
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
|
||||
const [currentCollection, setCurrentCollection] = useState(collectionField?.target);
|
||||
const addbuttonClick = (name) => {
|
||||
insertAddNewer(schema.AddNewer);
|
||||
setVisibleAddNewer(true);
|
||||
setCurrentCollection(name);
|
||||
};
|
||||
return (
|
||||
<CollectionProvider name={collectionField?.target}>
|
||||
<CreateAction {...props} onClick={(arg) => addbuttonClick(arg)} />
|
||||
<ActionContext.Provider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
|
||||
<CollectionProvider name={currentCollection}>
|
||||
<RecursionField
|
||||
onlyRenderProperties
|
||||
basePath={field.address}
|
||||
schema={fieldSchema}
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'AssociationField.AddNewer';
|
||||
}}
|
||||
/>
|
||||
</CollectionProvider>
|
||||
</ActionContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'CreateRecordAction' },
|
||||
);
|
||||
|
@ -4,8 +4,11 @@ import { useFieldTitle } from '../../hooks';
|
||||
import { RemoteSelect } from '../remote-select';
|
||||
import useServiceOptions from './useServiceOptions';
|
||||
|
||||
export const ReadPretty = observer((props: any) => {
|
||||
const service = useServiceOptions(props);
|
||||
useFieldTitle();
|
||||
return <RemoteSelect.ReadPretty {...props} service={service}></RemoteSelect.ReadPretty>;
|
||||
});
|
||||
export const ReadPretty = observer(
|
||||
(props: any) => {
|
||||
const service = useServiceOptions(props);
|
||||
useFieldTitle();
|
||||
return <RemoteSelect.ReadPretty {...props} service={service}></RemoteSelect.ReadPretty>;
|
||||
},
|
||||
{ displayName: 'ReadPretty' },
|
||||
);
|
||||
|
@ -3,15 +3,18 @@ import { uid } from '@formily/shared';
|
||||
import { BlockItem, DndContext, DragHandler, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import React from 'react';
|
||||
|
||||
const Block = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||
Block {fieldSchema.name}
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const Block = observer(
|
||||
(props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||
Block {fieldSchema.name}
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Block' },
|
||||
);
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
@ -190,71 +190,74 @@ const CalendarRecordViewer = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Calendar: any = observer((props: any) => {
|
||||
const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props);
|
||||
const [date, setDate] = useState<Date>(new Date());
|
||||
const [view, setView] = useState<(typeof Weeks)[number]>('month');
|
||||
const events = useEvents(dataSource, fieldNames, date, view);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [record, setRecord] = useState<any>({});
|
||||
export const Calendar: any = observer(
|
||||
(props: any) => {
|
||||
const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props);
|
||||
const [date, setDate] = useState<Date>(new Date());
|
||||
const [view, setView] = useState<(typeof Weeks)[number]>('month');
|
||||
const events = useEvents(dataSource, fieldNames, date, view);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [record, setRecord] = useState<any>({});
|
||||
|
||||
const components = useMemo(() => {
|
||||
return {
|
||||
toolbar: (props) => <Toolbar {...props} showLunar={showLunar}></Toolbar>,
|
||||
week: {
|
||||
header: (props) => <Header {...props} type="week" showLunar={showLunar}></Header>,
|
||||
},
|
||||
month: {
|
||||
dateHeader: (props) => <Header {...props} showLunar={showLunar}></Header>,
|
||||
},
|
||||
};
|
||||
}, [showLunar]);
|
||||
const components = useMemo(() => {
|
||||
return {
|
||||
toolbar: (props) => <Toolbar {...props} showLunar={showLunar}></Toolbar>,
|
||||
week: {
|
||||
header: (props) => <Header {...props} type="week" showLunar={showLunar}></Header>,
|
||||
},
|
||||
month: {
|
||||
dateHeader: (props) => <Header {...props} showLunar={showLunar}></Header>,
|
||||
},
|
||||
};
|
||||
}, [showLunar]);
|
||||
|
||||
return (
|
||||
<div style={{ height: fixedBlock ? '100%' : 700 }}>
|
||||
<CalendarRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
||||
<BigCalendar
|
||||
popup
|
||||
selectable
|
||||
events={events}
|
||||
view={view}
|
||||
views={Weeks}
|
||||
date={date}
|
||||
step={60}
|
||||
showMultiDayTimes
|
||||
messages={messages}
|
||||
onNavigate={setDate}
|
||||
onView={setView}
|
||||
onSelectSlot={(slotInfo) => {
|
||||
console.log('onSelectSlot', slotInfo);
|
||||
}}
|
||||
onDoubleClickEvent={(event) => {
|
||||
console.log('onDoubleClickEvent');
|
||||
}}
|
||||
onSelectEvent={(event) => {
|
||||
const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
record.__event = { ...event, start: formatDate(moment(event.start)), end: formatDate(moment(event.end)) };
|
||||
|
||||
setRecord(record);
|
||||
setVisible(true);
|
||||
}}
|
||||
formats={{
|
||||
monthHeaderFormat: 'Y-M',
|
||||
agendaDateFormat: 'M-DD',
|
||||
dayHeaderFormat: 'Y-M-DD',
|
||||
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
||||
if (dates.eq(start, end, 'month')) {
|
||||
return local.format(start, 'Y-M', culture);
|
||||
return (
|
||||
<div style={{ height: fixedBlock ? '100%' : 700 }}>
|
||||
<CalendarRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
||||
<BigCalendar
|
||||
popup
|
||||
selectable
|
||||
events={events}
|
||||
view={view}
|
||||
views={Weeks}
|
||||
date={date}
|
||||
step={60}
|
||||
showMultiDayTimes
|
||||
messages={messages}
|
||||
onNavigate={setDate}
|
||||
onView={setView}
|
||||
onSelectSlot={(slotInfo) => {
|
||||
console.log('onSelectSlot', slotInfo);
|
||||
}}
|
||||
onDoubleClickEvent={(event) => {
|
||||
console.log('onDoubleClickEvent');
|
||||
}}
|
||||
onSelectEvent={(event) => {
|
||||
const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
return `${local.format(start, 'Y-M', culture)} - ${local.format(end, 'Y-M', culture)}`;
|
||||
},
|
||||
}}
|
||||
components={components}
|
||||
localizer={localizer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
record.__event = { ...event, start: formatDate(moment(event.start)), end: formatDate(moment(event.end)) };
|
||||
|
||||
setRecord(record);
|
||||
setVisible(true);
|
||||
}}
|
||||
formats={{
|
||||
monthHeaderFormat: 'Y-M',
|
||||
agendaDateFormat: 'M-DD',
|
||||
dayHeaderFormat: 'Y-M-DD',
|
||||
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
||||
if (dates.eq(start, end, 'month')) {
|
||||
return local.format(start, 'Y-M', culture);
|
||||
}
|
||||
return `${local.format(start, 'Y-M', culture)} - ${local.format(end, 'Y-M', culture)}`;
|
||||
},
|
||||
}}
|
||||
components={components}
|
||||
localizer={localizer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Calendar' },
|
||||
);
|
||||
|
@ -11,61 +11,64 @@ import { DeleteEventContext } from './Calendar';
|
||||
import { formatDate } from './utils';
|
||||
const { Text } = Typography;
|
||||
|
||||
export const DeleteEvent = observer(() => {
|
||||
const { visible, setVisible } = useActionContext();
|
||||
const { exclude = [], cron, ...record } = useRecord();
|
||||
const { close } = useContext(DeleteEventContext);
|
||||
const startDate = formatDate(moment(record.__parent.__event.start));
|
||||
const filterByTk = useFilterByTk();
|
||||
const { resource, service, __parent } = useBlockRequestContext();
|
||||
const [value, onChange] = useState(startDate);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onOk = async () => {
|
||||
setLoading(true);
|
||||
if (value === 'all' || !cron) {
|
||||
await resource.destroy({
|
||||
filterByTk,
|
||||
});
|
||||
} else {
|
||||
await resource.update({
|
||||
filterByTk,
|
||||
values: {
|
||||
exclude: (exclude || []).concat(value),
|
||||
},
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
__parent?.service?.refresh?.();
|
||||
service?.refresh?.();
|
||||
setVisible?.(false, true);
|
||||
close();
|
||||
};
|
||||
export const DeleteEvent = observer(
|
||||
() => {
|
||||
const { visible, setVisible } = useActionContext();
|
||||
const { exclude = [], cron, ...record } = useRecord();
|
||||
const { close } = useContext(DeleteEventContext);
|
||||
const startDate = formatDate(moment(record.__parent.__event.start));
|
||||
const filterByTk = useFilterByTk();
|
||||
const { resource, service, __parent } = useBlockRequestContext();
|
||||
const [value, onChange] = useState(startDate);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onOk = async () => {
|
||||
setLoading(true);
|
||||
if (value === 'all' || !cron) {
|
||||
await resource.destroy({
|
||||
filterByTk,
|
||||
});
|
||||
} else {
|
||||
await resource.update({
|
||||
filterByTk,
|
||||
values: {
|
||||
exclude: (exclude || []).concat(value),
|
||||
},
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
__parent?.service?.refresh?.();
|
||||
service?.refresh?.();
|
||||
setVisible?.(false, true);
|
||||
close();
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
return createPortal(
|
||||
<Modal
|
||||
title={cron ? t('Delete events') : null}
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
onOk={() => onOk()}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
{cron ? (
|
||||
<Radio.Group value={value} onChange={(event) => onChange(event.target.value)}>
|
||||
<Space direction="vertical">
|
||||
<Radio value={startDate}>{t('This event')}</Radio>
|
||||
<Radio value={`${startDate}_after`}>{t('This and following events')}</Radio>
|
||||
<Radio value="all">{t('All events')}</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
) : (
|
||||
<Text strong style={{ fontSize: '18px' }}>
|
||||
{t('Delete this event?')}
|
||||
</Text>
|
||||
)}
|
||||
</Modal>,
|
||||
document.body,
|
||||
);
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
return createPortal(
|
||||
<Modal
|
||||
title={cron ? t('Delete events') : null}
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
onOk={() => onOk()}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
{cron ? (
|
||||
<Radio.Group value={value} onChange={(event) => onChange(event.target.value)}>
|
||||
<Space direction="vertical">
|
||||
<Radio value={startDate}>{t('This event')}</Radio>
|
||||
<Radio value={`${startDate}_after`}>{t('This and following events')}</Radio>
|
||||
<Radio value="all">{t('All events')}</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
) : (
|
||||
<Text strong style={{ fontSize: '18px' }}>
|
||||
{t('Delete this event?')}
|
||||
</Text>
|
||||
)}
|
||||
</Modal>,
|
||||
document.body,
|
||||
);
|
||||
},
|
||||
{ displayName: 'DeleteEvent' },
|
||||
);
|
||||
|
||||
export default DeleteEvent;
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { observer } from '@formily/react';
|
||||
import React from 'react';
|
||||
|
||||
export const Event = observer((props) => {
|
||||
return <>{props.children}</>;
|
||||
});
|
||||
export const Event = observer(
|
||||
(props) => {
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
{ displayName: 'Event' },
|
||||
);
|
||||
|
@ -6,14 +6,17 @@ import { navigate } from 'react-big-calendar/lib/utils/constants';
|
||||
import { useDesignable } from '../../hooks';
|
||||
import { CalendarToolbarContext } from './context';
|
||||
|
||||
export const Nav = observer((props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { onNavigate } = useContext(CalendarToolbarContext);
|
||||
return (
|
||||
<div className="ant-btn-group">
|
||||
<Button icon={<LeftOutlined />} onClick={() => onNavigate(navigate.PREVIOUS)}></Button>
|
||||
<Button icon={<RightOutlined />} onClick={() => onNavigate(navigate.NEXT)}></Button>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const Nav = observer(
|
||||
(props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { onNavigate } = useContext(CalendarToolbarContext);
|
||||
return (
|
||||
<div className="ant-btn-group">
|
||||
<Button icon={<LeftOutlined />} onClick={() => onNavigate(navigate.PREVIOUS)}></Button>
|
||||
<Button icon={<RightOutlined />} onClick={() => onNavigate(navigate.NEXT)}></Button>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Nav' },
|
||||
);
|
||||
|
@ -4,22 +4,25 @@ import { useDesignable } from '../../hooks';
|
||||
import { CalendarToolbarContext } from './context';
|
||||
import { getLunarDay } from './utils';
|
||||
|
||||
export const Title = observer(() => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { date, view, label, showLunar } = useContext(CalendarToolbarContext);
|
||||
export const Title = observer(
|
||||
() => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { date, view, label, showLunar } = useContext(CalendarToolbarContext);
|
||||
|
||||
const lunarElement = useMemo(() => {
|
||||
if (!showLunar || view !== 'day') {
|
||||
return;
|
||||
}
|
||||
return <span>{getLunarDay(date)}</span>;
|
||||
}, [view, date, showLunar]);
|
||||
const lunarElement = useMemo(() => {
|
||||
if (!showLunar || view !== 'day') {
|
||||
return;
|
||||
}
|
||||
return <span>{getLunarDay(date)}</span>;
|
||||
}, [view, date, showLunar]);
|
||||
|
||||
return (
|
||||
<div className="ant-btn-group" style={{ fontSize: '1.75em', fontWeight: 300 }}>
|
||||
<span>{label}</span>
|
||||
<span style={{ marginLeft: '4px' }}>{lunarElement}</span>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className="ant-btn-group" style={{ fontSize: '1.75em', fontWeight: 300 }}>
|
||||
<span>{label}</span>
|
||||
<span style={{ marginLeft: '4px' }}>{lunarElement}</span>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Title' },
|
||||
);
|
||||
|
@ -6,18 +6,21 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useDesignable } from '../../hooks';
|
||||
import { CalendarToolbarContext } from './context';
|
||||
|
||||
export const Today = observer((props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { onNavigate } = useContext(CalendarToolbarContext);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onNavigate(navigate.TODAY);
|
||||
}}
|
||||
>
|
||||
{t('Today')}
|
||||
<DesignableBar />
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
export const Today = observer(
|
||||
(props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { onNavigate } = useContext(CalendarToolbarContext);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onNavigate(navigate.TODAY);
|
||||
}}
|
||||
>
|
||||
{t('Today')}
|
||||
<DesignableBar />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Today' },
|
||||
);
|
||||
|
@ -4,22 +4,25 @@ import React, { useContext } from 'react';
|
||||
import { useDesignable } from '../../hooks';
|
||||
import { CalendarToolbarContext } from './context';
|
||||
|
||||
export const ViewSelect = observer((props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const {
|
||||
views,
|
||||
view,
|
||||
onView,
|
||||
localizer: { messages },
|
||||
} = useContext(CalendarToolbarContext);
|
||||
return (
|
||||
<div className="ant-btn-group">
|
||||
<Select value={view} onChange={onView}>
|
||||
{views.map((name) => (
|
||||
<Select.Option value={name}>{messages[name]}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const ViewSelect = observer(
|
||||
(props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const {
|
||||
views,
|
||||
view,
|
||||
onView,
|
||||
localizer: { messages },
|
||||
} = useContext(CalendarToolbarContext);
|
||||
return (
|
||||
<div className="ant-btn-group">
|
||||
<Select value={view} onChange={onView}>
|
||||
{views.map((name) => (
|
||||
<Select.Option value={name}>{messages[name]}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ViewSelect' },
|
||||
);
|
||||
|
@ -58,6 +58,6 @@ export const CollectionSelect = connect(
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
}, { displayName: 'CollectionSelectObserver' }),
|
||||
),
|
||||
);
|
||||
|
@ -15,35 +15,38 @@ const useDef = (options) => {
|
||||
return useRequest(() => Promise.resolve({ data: field.dataSource }), options);
|
||||
};
|
||||
|
||||
export const Filter: any = observer((props: any) => {
|
||||
const { useDataSource = useDef } = props;
|
||||
const { options, dynamicComponent, className } = useProps(props);
|
||||
const field = useField<ObjectFieldModel>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
useDataSource({
|
||||
onSuccess(data) {
|
||||
field.dataSource = data?.data || [];
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div className={className}>
|
||||
<DatePickerProvider value={{ utc: false }}>
|
||||
<FilterContext.Provider
|
||||
value={{
|
||||
field,
|
||||
fieldSchema,
|
||||
dynamicComponent,
|
||||
options: options || field.dataSource || [],
|
||||
disabled: props.disabled,
|
||||
}}
|
||||
>
|
||||
<FilterGroup {...props} bordered={false} />
|
||||
{/* <pre>{JSON.stringify(field.value, null, 2)}</pre> */}
|
||||
</FilterContext.Provider>
|
||||
</DatePickerProvider>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const Filter: any = observer(
|
||||
(props: any) => {
|
||||
const { useDataSource = useDef } = props;
|
||||
const { options, dynamicComponent, className } = useProps(props);
|
||||
const field = useField<ObjectFieldModel>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
useDataSource({
|
||||
onSuccess(data) {
|
||||
field.dataSource = data?.data || [];
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div className={className}>
|
||||
<DatePickerProvider value={{ utc: false }}>
|
||||
<FilterContext.Provider
|
||||
value={{
|
||||
field,
|
||||
fieldSchema,
|
||||
dynamicComponent,
|
||||
options: options || field.dataSource || [],
|
||||
disabled: props.disabled,
|
||||
}}
|
||||
>
|
||||
<FilterGroup {...props} bordered={false} />
|
||||
{/* <pre>{JSON.stringify(field.value, null, 2)}</pre> */}
|
||||
</FilterContext.Provider>
|
||||
</DatePickerProvider>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Filter' },
|
||||
);
|
||||
|
||||
Filter.SaveDefaultValue = SaveDefaultValue;
|
||||
|
||||
|
@ -11,83 +11,86 @@ import { Action } from '../action';
|
||||
|
||||
export const FilterActionContext = createContext<any>(null);
|
||||
|
||||
export const FilterAction = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
const field = useField<Field>();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { designable, dn } = useDesignable();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo<Form>(() => props.form || createForm(), []);
|
||||
const { options, onSubmit, onReset, ...others } = useProps(props);
|
||||
return (
|
||||
<FilterActionContext.Provider value={{ field, fieldSchema, designable, dn }}>
|
||||
<Popover
|
||||
destroyTooltipOnHide
|
||||
placement={'bottomLeft'}
|
||||
open={visible}
|
||||
onOpenChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
trigger={'click'}
|
||||
content={
|
||||
<form>
|
||||
<FormProvider form={form}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'string',
|
||||
enum: options || field.dataSource,
|
||||
default: fieldSchema.default,
|
||||
'x-component': 'Filter',
|
||||
'x-component-props': {},
|
||||
export const FilterAction = observer(
|
||||
(props: any) => {
|
||||
const { t } = useTranslation();
|
||||
const field = useField<Field>();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { designable, dn } = useDesignable();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo<Form>(() => props.form || createForm(), []);
|
||||
const { options, onSubmit, onReset, ...others } = useProps(props);
|
||||
return (
|
||||
<FilterActionContext.Provider value={{ field, fieldSchema, designable, dn }}>
|
||||
<Popover
|
||||
destroyTooltipOnHide
|
||||
placement={'bottomLeft'}
|
||||
open={visible}
|
||||
onOpenChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
trigger={'click'}
|
||||
content={
|
||||
<form>
|
||||
<FormProvider form={form}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'string',
|
||||
enum: options || field.dataSource,
|
||||
default: fieldSchema.default,
|
||||
'x-component': 'Filter',
|
||||
'x-component-props': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
<Space>
|
||||
<SaveConditions />
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await form.reset();
|
||||
onReset?.(form.values);
|
||||
field.title = t('Filter');
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
{t('Reset')}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
htmlType={'submit'}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onSubmit?.(form.values);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</FormProvider>
|
||||
</form>
|
||||
}
|
||||
>
|
||||
<Action {...others} title={field.title} />
|
||||
</Popover>
|
||||
</FilterActionContext.Provider>
|
||||
);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
<Space>
|
||||
<SaveConditions />
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await form.reset();
|
||||
onReset?.(form.values);
|
||||
field.title = t('Filter');
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
{t('Reset')}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
htmlType={'submit'}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onSubmit?.(form.values);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</FormProvider>
|
||||
</form>
|
||||
}
|
||||
>
|
||||
<Action {...others} title={field.title} />
|
||||
</Popover>
|
||||
</FilterActionContext.Provider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'FilterAction' },
|
||||
);
|
||||
|
||||
const SaveConditions = () => {
|
||||
const { fieldSchema, field, designable, dn } = useContext(FilterActionContext);
|
||||
|
@ -9,56 +9,59 @@ import { RemoveConditionContext } from './context';
|
||||
import { DynamicComponent } from './DynamicComponent';
|
||||
import { useValues } from './useValues';
|
||||
|
||||
export const FilterItem = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
const compile = useCompile();
|
||||
const remove = useContext(RemoveConditionContext);
|
||||
const { schema, fields, operators, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues();
|
||||
return (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Space>
|
||||
<Cascader
|
||||
className={css`
|
||||
width: 160px;
|
||||
`}
|
||||
fieldNames={{
|
||||
label: 'title',
|
||||
value: 'name',
|
||||
children: 'children',
|
||||
}}
|
||||
changeOnSelect={false}
|
||||
value={dataIndex}
|
||||
options={compile(fields)}
|
||||
onChange={(value) => {
|
||||
setDataIndex(value);
|
||||
}}
|
||||
placeholder={t('Select Field')}
|
||||
/>
|
||||
<Select
|
||||
className={css`
|
||||
min-width: 110px;
|
||||
`}
|
||||
value={operator?.value}
|
||||
options={compile(operators)}
|
||||
onChange={(value) => {
|
||||
setOperator(value);
|
||||
}}
|
||||
placeholder={t('Comparision')}
|
||||
/>
|
||||
{!operator?.noValue &&
|
||||
React.createElement(DynamicComponent, {
|
||||
value: value ? value : undefined,
|
||||
schema,
|
||||
onChange(value) {
|
||||
setValue(value);
|
||||
},
|
||||
})}
|
||||
{!props.disabled && (
|
||||
<a>
|
||||
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
|
||||
</a>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const FilterItem = observer(
|
||||
(props: any) => {
|
||||
const { t } = useTranslation();
|
||||
const compile = useCompile();
|
||||
const remove = useContext(RemoveConditionContext);
|
||||
const { schema, fields, operators, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues();
|
||||
return (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Space>
|
||||
<Cascader
|
||||
className={css`
|
||||
width: 160px;
|
||||
`}
|
||||
fieldNames={{
|
||||
label: 'title',
|
||||
value: 'name',
|
||||
children: 'children',
|
||||
}}
|
||||
changeOnSelect={false}
|
||||
value={dataIndex}
|
||||
options={compile(fields)}
|
||||
onChange={(value) => {
|
||||
setDataIndex(value);
|
||||
}}
|
||||
placeholder={t('Select Field')}
|
||||
/>
|
||||
<Select
|
||||
className={css`
|
||||
min-width: 110px;
|
||||
`}
|
||||
value={operator?.value}
|
||||
options={compile(operators)}
|
||||
onChange={(value) => {
|
||||
setOperator(value);
|
||||
}}
|
||||
placeholder={t('Comparision')}
|
||||
/>
|
||||
{!operator?.noValue &&
|
||||
React.createElement(DynamicComponent, {
|
||||
value: value ? value : undefined,
|
||||
schema,
|
||||
onChange(value) {
|
||||
setValue(value);
|
||||
},
|
||||
})}
|
||||
{!props.disabled && (
|
||||
<a>
|
||||
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
|
||||
</a>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'FilterItem' },
|
||||
);
|
||||
|
@ -5,17 +5,20 @@ import { RemoveConditionContext } from './context';
|
||||
import { FilterGroup } from './FilterGroup';
|
||||
import { FilterItem } from './FilterItem';
|
||||
|
||||
export const FilterItems = observer((props) => {
|
||||
const field = useField<ArrayFieldModel>();
|
||||
return (
|
||||
<div>
|
||||
{field?.value?.map((item, index) => {
|
||||
return (
|
||||
<RemoveConditionContext.Provider key={index} value={() => field.remove(index)}>
|
||||
<ObjectField name={index} component={[item.$and || item.$or ? FilterGroup : FilterItem]} />
|
||||
</RemoveConditionContext.Provider>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const FilterItems = observer(
|
||||
(props) => {
|
||||
const field = useField<ArrayFieldModel>();
|
||||
return (
|
||||
<div>
|
||||
{field?.value?.map((item, index) => {
|
||||
return (
|
||||
<RemoveConditionContext.Provider key={index} value={() => field.remove(index)}>
|
||||
<ObjectField name={index} component={[item.$and || item.$or ? FilterGroup : FilterItem]} />
|
||||
</RemoveConditionContext.Provider>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'FilterItems' },
|
||||
);
|
||||
|
@ -53,61 +53,64 @@ const divWrap = (schema: ISchema) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const FormItem: any = observer((props: any) => {
|
||||
useEnsureOperatorsValid();
|
||||
export const FormItem: any = observer(
|
||||
(props: any) => {
|
||||
useEnsureOperatorsValid();
|
||||
|
||||
const field = useField<Field>();
|
||||
const ctx = useContext(BlockRequestContext);
|
||||
const schema = useFieldSchema();
|
||||
const variablesCtx = useVariablesCtx();
|
||||
const field = useField<Field>();
|
||||
const ctx = useContext(BlockRequestContext);
|
||||
const schema = useFieldSchema();
|
||||
const variablesCtx = useVariablesCtx();
|
||||
|
||||
useEffect(() => {
|
||||
if (ctx?.block === 'form') {
|
||||
ctx.field.data = ctx.field.data || {};
|
||||
ctx.field.data.activeFields = ctx.field.data.activeFields || new Set();
|
||||
ctx.field.data.activeFields.add(schema.name);
|
||||
// 如果默认值是一个变量,则需要解析之后再显示出来
|
||||
if (isVariable(schema?.default)) {
|
||||
field.setInitialValue?.(parseVariables(schema.default, variablesCtx));
|
||||
useEffect(() => {
|
||||
if (ctx?.block === 'form') {
|
||||
ctx.field.data = ctx.field.data || {};
|
||||
ctx.field.data.activeFields = ctx.field.data.activeFields || new Set();
|
||||
ctx.field.data.activeFields.add(schema.name);
|
||||
// 如果默认值是一个变量,则需要解析之后再显示出来
|
||||
if (isVariable(schema?.default)) {
|
||||
field.setInitialValue?.(parseVariables(schema.default, variablesCtx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
const showTitle = schema['x-decorator-props']?.showTitle ?? true;
|
||||
return (
|
||||
<ACLCollectionFieldProvider>
|
||||
<BlockItem className={'nb-form-item'}>
|
||||
<Item
|
||||
className={cx(
|
||||
css`
|
||||
& .ant-space {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`,
|
||||
{
|
||||
[css`
|
||||
> .ant-formily-item-label {
|
||||
display: none;
|
||||
}, []);
|
||||
const showTitle = schema['x-decorator-props']?.showTitle ?? true;
|
||||
return (
|
||||
<ACLCollectionFieldProvider>
|
||||
<BlockItem className={'nb-form-item'}>
|
||||
<Item
|
||||
className={cx(
|
||||
css`
|
||||
& .ant-space {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`]: showTitle === false,
|
||||
},
|
||||
)}
|
||||
{...props}
|
||||
extra={
|
||||
typeof field.description === 'string' ? (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
field.description
|
||||
)
|
||||
}
|
||||
/>
|
||||
</BlockItem>
|
||||
</ACLCollectionFieldProvider>
|
||||
);
|
||||
});
|
||||
`,
|
||||
{
|
||||
[css`
|
||||
> .ant-formily-item-label {
|
||||
display: none;
|
||||
}
|
||||
`]: showTitle === false,
|
||||
},
|
||||
)}
|
||||
{...props}
|
||||
extra={
|
||||
typeof field.description === 'string' ? (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
field.description
|
||||
)
|
||||
}
|
||||
/>
|
||||
</BlockItem>
|
||||
</ACLCollectionFieldProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'FormItem' },
|
||||
);
|
||||
|
||||
FormItem.Designer = function Designer() {
|
||||
const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
|
||||
|
@ -170,21 +170,24 @@ export const Form: React.FC<FormProps> & {
|
||||
FilterDesigner?: any;
|
||||
ReadPrettyDesigner?: any;
|
||||
Templates?: any;
|
||||
} = observer((props) => {
|
||||
const field = useField<Field>();
|
||||
const { form, disabled, ...others } = useProps(props);
|
||||
const formDisabled = disabled || field.disabled;
|
||||
return (
|
||||
<ConfigProvider componentDisabled={formDisabled}>
|
||||
<form>
|
||||
<Spin spinning={field.loading || false}>
|
||||
{form ? (
|
||||
<WithForm form={form} {...others} disabled={formDisabled} />
|
||||
) : (
|
||||
<WithoutForm {...others} disabled={formDisabled} />
|
||||
)}
|
||||
</Spin>
|
||||
</form>
|
||||
</ConfigProvider>
|
||||
);
|
||||
});
|
||||
} = observer(
|
||||
(props) => {
|
||||
const field = useField<Field>();
|
||||
const { form, disabled, ...others } = useProps(props);
|
||||
const formDisabled = disabled || field.disabled;
|
||||
return (
|
||||
<ConfigProvider componentDisabled={formDisabled}>
|
||||
<form>
|
||||
<Spin spinning={field.loading || false}>
|
||||
{form ? (
|
||||
<WithForm form={form} {...others} disabled={formDisabled} />
|
||||
) : (
|
||||
<WithoutForm {...others} disabled={formDisabled} />
|
||||
)}
|
||||
</Spin>
|
||||
</form>
|
||||
</ConfigProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Form' },
|
||||
);
|
||||
|
@ -4,21 +4,24 @@ import { useFormBlockContext } from '../../../block-provider';
|
||||
import { useCollection } from '../../../collection-manager';
|
||||
import { useCompile } from '../../hooks';
|
||||
|
||||
export const FormField: any = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { getField } = useCollection();
|
||||
const field = useField();
|
||||
const collectionField = getField(fieldSchema.name);
|
||||
const compile = useCompile();
|
||||
const ctx = useFormBlockContext();
|
||||
useEffect(() => {
|
||||
if (!field.title) {
|
||||
field.title = compile(collectionField?.uiSchema?.title);
|
||||
}
|
||||
if (ctx?.field) {
|
||||
ctx.field.added = ctx.field.added || new Set();
|
||||
ctx.field.added.add(fieldSchema.name);
|
||||
}
|
||||
}, []);
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
export const FormField: any = observer(
|
||||
(props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { getField } = useCollection();
|
||||
const field = useField();
|
||||
const collectionField = getField(fieldSchema.name);
|
||||
const compile = useCompile();
|
||||
const ctx = useFormBlockContext();
|
||||
useEffect(() => {
|
||||
if (!field.title) {
|
||||
field.title = compile(collectionField?.uiSchema?.title);
|
||||
}
|
||||
if (ctx?.field) {
|
||||
ctx.field.added = ctx.field.added || new Set();
|
||||
ctx.field.added.add(fieldSchema.name);
|
||||
}
|
||||
}, []);
|
||||
return <div>{props.children}</div>;
|
||||
},
|
||||
{ displayName: 'FormField' },
|
||||
);
|
||||
|
@ -83,34 +83,37 @@ const useDefaultValues = (opts: any = {}, props: FormProps = {}) => {
|
||||
|
||||
const FormBlockContext = createContext<any>(null);
|
||||
|
||||
export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) => {
|
||||
const { request, effects, initialValue, useValues = useDefaultValues, ...others } = props;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const form = useMemo(() => createForm({ effects }), []);
|
||||
const result = useValues(
|
||||
{
|
||||
uid: fieldSchema['x-uid'],
|
||||
async onSuccess(data) {
|
||||
await form.reset();
|
||||
form.setValues(data?.data);
|
||||
export const Form: React.FC<FormProps> & { Designer?: any } = observer(
|
||||
(props) => {
|
||||
const { request, effects, initialValue, useValues = useDefaultValues, ...others } = props;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const form = useMemo(() => createForm({ effects }), []);
|
||||
const result = useValues(
|
||||
{
|
||||
uid: fieldSchema['x-uid'],
|
||||
async onSuccess(data) {
|
||||
await form.reset();
|
||||
form.setValues(data?.data);
|
||||
},
|
||||
},
|
||||
},
|
||||
props,
|
||||
);
|
||||
const parent = useContext(FormBlockContext);
|
||||
return (
|
||||
<FormBlockContext.Provider value={{ parent, form, result, field, fieldSchema }}>
|
||||
<Spin spinning={result?.loading || false}>
|
||||
{fieldSchema['x-decorator'] === 'Form' ? (
|
||||
<FormDecorator form={form} {...others} />
|
||||
) : (
|
||||
<FormComponent form={form} {...others} />
|
||||
)}
|
||||
</Spin>
|
||||
</FormBlockContext.Provider>
|
||||
);
|
||||
});
|
||||
props,
|
||||
);
|
||||
const parent = useContext(FormBlockContext);
|
||||
return (
|
||||
<FormBlockContext.Provider value={{ parent, form, result, field, fieldSchema }}>
|
||||
<Spin spinning={result?.loading || false}>
|
||||
{fieldSchema['x-decorator'] === 'Form' ? (
|
||||
<FormDecorator form={form} {...others} />
|
||||
) : (
|
||||
<FormComponent form={form} {...others} />
|
||||
)}
|
||||
</Spin>
|
||||
</FormBlockContext.Provider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Form' },
|
||||
);
|
||||
|
||||
Form.Designer = () => {
|
||||
const { name, title } = useCollection();
|
||||
|
@ -49,10 +49,13 @@ export default observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
return (
|
||||
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
|
||||
|
@ -38,10 +38,13 @@ export default observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
const useSubmit = () => {
|
||||
const form = useForm();
|
||||
|
@ -38,10 +38,13 @@ export default observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
const useSubmit = () => {
|
||||
const form = useForm();
|
||||
|
@ -54,10 +54,13 @@ export default observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
return (
|
||||
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
|
||||
|
@ -69,10 +69,13 @@ export default observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
const useSubmit = () => {
|
||||
const form = useForm();
|
||||
|
@ -39,10 +39,13 @@ export default observer(() => {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
const useSubmit = () => {
|
||||
const form = useForm();
|
||||
|
@ -36,10 +36,13 @@ const schema: ISchema = {
|
||||
},
|
||||
};
|
||||
|
||||
const Output = observer(() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
});
|
||||
const Output = observer(
|
||||
() => {
|
||||
const form = useForm();
|
||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||
},
|
||||
{ displayName: 'Output' },
|
||||
);
|
||||
|
||||
const useSubmit = () => {
|
||||
const form = useForm();
|
||||
|
@ -127,51 +127,54 @@ export const G2PlotRenderer = forwardRef(function <O = any>(props: ReactG2PlotPr
|
||||
return <div className={cls(['g2plot', className])} ref={containerRef} />;
|
||||
});
|
||||
|
||||
export const G2Plot: any = observer((props: any) => {
|
||||
const { plot, config } = props;
|
||||
const field = useField<Field>();
|
||||
const { t } = useTranslation();
|
||||
const api = useAPIClient();
|
||||
useEffect(() => {
|
||||
field.data = field.data || {};
|
||||
field.data.loading = true;
|
||||
const fn = config?.data;
|
||||
if (typeof fn === 'function') {
|
||||
const result = fn.bind({ api })();
|
||||
if (result?.then) {
|
||||
result.then((data) => {
|
||||
if (Array.isArray(data)) {
|
||||
field.componentProps.config.data = data;
|
||||
}
|
||||
export const G2Plot: any = observer(
|
||||
(props: any) => {
|
||||
const { plot, config } = props;
|
||||
const field = useField<Field>();
|
||||
const { t } = useTranslation();
|
||||
const api = useAPIClient();
|
||||
useEffect(() => {
|
||||
field.data = field.data || {};
|
||||
field.data.loading = true;
|
||||
const fn = config?.data;
|
||||
if (typeof fn === 'function') {
|
||||
const result = fn.bind({ api })();
|
||||
if (result?.then) {
|
||||
result.then((data) => {
|
||||
if (Array.isArray(data)) {
|
||||
field.componentProps.config.data = data;
|
||||
}
|
||||
field.data.loading = false;
|
||||
});
|
||||
} else {
|
||||
field.data.loading = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
field.data.loading = false;
|
||||
}
|
||||
} else {
|
||||
field.data.loading = false;
|
||||
}
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
if (!plot || !config) {
|
||||
return <div style={{ opacity: 0.3 }}>{t('In configuration')}...</div>;
|
||||
}
|
||||
if (field?.data?.loading !== false) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{field.title && <h2>{field.title}</h2>}
|
||||
<G2PlotRenderer
|
||||
plot={plots[plot]}
|
||||
config={{
|
||||
...config,
|
||||
data: Array.isArray(config?.data) ? config.data : [],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
if (!plot || !config) {
|
||||
return <div style={{ opacity: 0.3 }}>{t('In configuration')}...</div>;
|
||||
}
|
||||
if (field?.data?.loading !== false) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{field.title && <h2>{field.title}</h2>}
|
||||
<G2PlotRenderer
|
||||
plot={plots[plot]}
|
||||
config={{
|
||||
...config,
|
||||
data: Array.isArray(config?.data) ? config.data : [],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'G2Plot' },
|
||||
);
|
||||
|
||||
G2Plot.Designer = G2PlotDesigner;
|
||||
G2Plot.plots = plots;
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { observer } from '@formily/react';
|
||||
import React from 'react';
|
||||
|
||||
export const Event = observer((props) => {
|
||||
return <>{props.children}</>;
|
||||
});
|
||||
export const Event = observer(
|
||||
(props) => {
|
||||
return <>{props.children}</>;
|
||||
},
|
||||
{ displayName: 'Event' },
|
||||
);
|
||||
|
@ -2,12 +2,15 @@ import { observer, useFieldSchema } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { DragHandler } from '../../';
|
||||
|
||||
export const Block = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||
Block {fieldSchema.title}
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const Block = observer(
|
||||
(props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||
Block {fieldSchema.title}
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Block' },
|
||||
);
|
||||
|
@ -330,45 +330,108 @@ export const useGridRowContext = () => {
|
||||
return useContext(GridRowContext);
|
||||
};
|
||||
|
||||
export const Grid: any = observer((props: any) => {
|
||||
const gridRef = useRef(null);
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render, InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
const addr = field.address.toString();
|
||||
const rows = useRowProperties();
|
||||
const { setPrintContent } = useFormBlockContext();
|
||||
export const Grid: any = observer(
|
||||
(props: any) => {
|
||||
const gridRef = useRef(null);
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render, InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
const addr = field.address.toString();
|
||||
const rows = useRowProperties();
|
||||
const { setPrintContent } = useFormBlockContext();
|
||||
|
||||
useEffect(() => {
|
||||
gridRef.current && setPrintContent?.(gridRef.current);
|
||||
}, [gridRef.current]);
|
||||
useEffect(() => {
|
||||
gridRef.current && setPrintContent?.(gridRef.current);
|
||||
}, [gridRef.current]);
|
||||
|
||||
return (
|
||||
<GridContext.Provider value={{ ref: gridRef, fieldSchema, renderSchemaInitializer: render, InitializerComponent }}>
|
||||
<div className={'nb-grid'} style={{ position: 'relative' }} ref={gridRef}>
|
||||
<DndWrapper dndContext={props.dndContext}>
|
||||
<RowDivider
|
||||
rows={rows}
|
||||
return (
|
||||
<GridContext.Provider
|
||||
value={{ ref: gridRef, fieldSchema, renderSchemaInitializer: render, InitializerComponent }}
|
||||
>
|
||||
<div className={'nb-grid'} style={{ position: 'relative' }} ref={gridRef}>
|
||||
<DndWrapper dndContext={props.dndContext}>
|
||||
<RowDivider
|
||||
rows={rows}
|
||||
first
|
||||
id={`${addr}_0`}
|
||||
data={{
|
||||
breakRemoveOn: breakRemoveOnGrid,
|
||||
wrapSchema: wrapRowSchema,
|
||||
insertAdjacent: 'afterBegin',
|
||||
schema: fieldSchema,
|
||||
}}
|
||||
/>
|
||||
{rows.map((schema, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<RecursionField name={schema.name} schema={schema} />
|
||||
<RowDivider
|
||||
rows={rows}
|
||||
index={index}
|
||||
id={`${addr}_${index + 1}`}
|
||||
data={{
|
||||
breakRemoveOn: breakRemoveOnGrid,
|
||||
wrapSchema: wrapRowSchema,
|
||||
insertAdjacent: 'afterEnd',
|
||||
schema,
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</DndWrapper>
|
||||
<InitializerComponent />
|
||||
</div>
|
||||
</GridContext.Provider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Grid' },
|
||||
);
|
||||
|
||||
Grid.Row = observer(
|
||||
() => {
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const addr = field.address.toString();
|
||||
const cols = useColProperties();
|
||||
|
||||
return (
|
||||
<GridRowContext.Provider value={{ schema: fieldSchema, cols }}>
|
||||
<div
|
||||
className={cls(
|
||||
'nb-grid-row',
|
||||
css`
|
||||
overflow-x: hidden;
|
||||
margin: 0 calc(-1 * var(--nb-spacing));
|
||||
display: flex;
|
||||
position: relative;
|
||||
/* z-index: 0; */
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<ColDivider
|
||||
cols={cols}
|
||||
first
|
||||
id={`${addr}_0`}
|
||||
data={{
|
||||
breakRemoveOn: breakRemoveOnGrid,
|
||||
wrapSchema: wrapRowSchema,
|
||||
breakRemoveOn: breakRemoveOnRow,
|
||||
wrapSchema: wrapColSchema,
|
||||
insertAdjacent: 'afterBegin',
|
||||
schema: fieldSchema,
|
||||
}}
|
||||
/>
|
||||
{rows.map((schema, index) => {
|
||||
{cols.map((schema, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<RecursionField name={schema.name} schema={schema} />
|
||||
<RowDivider
|
||||
rows={rows}
|
||||
<ColDivider
|
||||
cols={cols}
|
||||
index={index}
|
||||
last={index === cols.length - 1}
|
||||
id={`${addr}_${index + 1}`}
|
||||
data={{
|
||||
breakRemoveOn: breakRemoveOnGrid,
|
||||
wrapSchema: wrapRowSchema,
|
||||
breakRemoveOn: breakRemoveOnRow,
|
||||
wrapSchema: wrapColSchema,
|
||||
insertAdjacent: 'afterEnd',
|
||||
schema,
|
||||
}}
|
||||
@ -376,95 +439,43 @@ export const Grid: any = observer((props: any) => {
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</DndWrapper>
|
||||
<InitializerComponent />
|
||||
</div>
|
||||
</GridContext.Provider>
|
||||
);
|
||||
});
|
||||
</div>
|
||||
</GridRowContext.Provider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Grid.Row' },
|
||||
);
|
||||
|
||||
Grid.Row = observer(() => {
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const addr = field.address.toString();
|
||||
const cols = useColProperties();
|
||||
Grid.Col = observer(
|
||||
(props: any) => {
|
||||
const { cols = [] } = useContext(GridRowContext);
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
|
||||
return (
|
||||
<GridRowContext.Provider value={{ schema: fieldSchema, cols }}>
|
||||
<div
|
||||
className={cls(
|
||||
'nb-grid-row',
|
||||
css`
|
||||
overflow-x: hidden;
|
||||
margin: 0 calc(-1 * var(--nb-spacing));
|
||||
display: flex;
|
||||
position: relative;
|
||||
/* z-index: 0; */
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<ColDivider
|
||||
cols={cols}
|
||||
first
|
||||
id={`${addr}_0`}
|
||||
data={{
|
||||
breakRemoveOn: breakRemoveOnRow,
|
||||
wrapSchema: wrapColSchema,
|
||||
insertAdjacent: 'afterBegin',
|
||||
schema: fieldSchema,
|
||||
}}
|
||||
/>
|
||||
{cols.map((schema, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<RecursionField name={schema.name} schema={schema} />
|
||||
<ColDivider
|
||||
cols={cols}
|
||||
index={index}
|
||||
last={index === cols.length - 1}
|
||||
id={`${addr}_${index + 1}`}
|
||||
data={{
|
||||
breakRemoveOn: breakRemoveOnRow,
|
||||
wrapSchema: wrapColSchema,
|
||||
insertAdjacent: 'afterEnd',
|
||||
schema,
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</GridRowContext.Provider>
|
||||
);
|
||||
});
|
||||
const width = useMemo(() => {
|
||||
let width = '';
|
||||
if (cols?.length) {
|
||||
const w = schema?.['x-component-props']?.['width'] || 100 / cols.length;
|
||||
width = `calc(${w}% - var(--nb-spacing) * ${(cols.length + 1) / cols.length})`;
|
||||
}
|
||||
return width;
|
||||
}, [cols?.length, schema?.['x-component-props']?.['width']]);
|
||||
|
||||
Grid.Col = observer((props: any) => {
|
||||
const { cols = [] } = useContext(GridRowContext);
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
|
||||
const width = useMemo(() => {
|
||||
let width = '';
|
||||
if (cols?.length) {
|
||||
const w = schema?.['x-component-props']?.['width'] || 100 / cols.length;
|
||||
width = `calc(${w}% - var(--nb-spacing) * ${(cols.length + 1) / cols.length})`;
|
||||
}
|
||||
return width;
|
||||
}, [cols?.length, schema?.['x-component-props']?.['width']]);
|
||||
|
||||
const { setNodeRef } = useDroppable({
|
||||
id: field.address.toString(),
|
||||
data: {
|
||||
insertAdjacent: 'beforeEnd',
|
||||
schema,
|
||||
wrapSchema: (s) => s,
|
||||
},
|
||||
});
|
||||
return (
|
||||
<GridColContext.Provider value={{ cols, schema }}>
|
||||
<div ref={setNodeRef} style={{ width }} className={cls('nb-grid-col')}>
|
||||
{props.children}
|
||||
</div>
|
||||
</GridColContext.Provider>
|
||||
);
|
||||
});
|
||||
const { setNodeRef } = useDroppable({
|
||||
id: field.address.toString(),
|
||||
data: {
|
||||
insertAdjacent: 'beforeEnd',
|
||||
schema,
|
||||
wrapSchema: (s) => s,
|
||||
},
|
||||
});
|
||||
return (
|
||||
<GridColContext.Provider value={{ cols, schema }}>
|
||||
<div ref={setNodeRef} style={{ width }} className={cls('nb-grid-col')}>
|
||||
{props.children}
|
||||
</div>
|
||||
</GridColContext.Provider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Grid.Col' },
|
||||
);
|
||||
|
@ -2,15 +2,18 @@ import { observer, useFieldSchema } from '@formily/react';
|
||||
import { BlockItem, DragHandler, Grid, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import React from 'react';
|
||||
|
||||
const Block = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||
Block {fieldSchema.title}
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const Block = observer(
|
||||
(props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||
Block {fieldSchema.title}
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Block' },
|
||||
);
|
||||
|
||||
const schema = {
|
||||
_isJSONSchemaObject: true,
|
||||
|
@ -10,98 +10,101 @@ import { RecordProvider } from '../../../record-provider';
|
||||
import { SchemaComponentOptions } from '../../core/SchemaComponentOptions';
|
||||
import { KanbanCardContext } from './context';
|
||||
|
||||
export const KanbanCard: any = observer((props: any) => {
|
||||
const { setDisableCardDrag, cardViewerSchema, card, cardField, columnIndex, cardIndex } =
|
||||
useContext(KanbanCardContext);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const labelDisabled = fieldSchema['x-label-disabled'];
|
||||
export const KanbanCard: any = observer(
|
||||
(props: any) => {
|
||||
const { setDisableCardDrag, cardViewerSchema, card, cardField, columnIndex, cardIndex } =
|
||||
useContext(KanbanCardContext);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const labelDisabled = fieldSchema['x-label-disabled'];
|
||||
|
||||
return (
|
||||
<SchemaComponentOptions components={{}} scope={{}}>
|
||||
<Card
|
||||
onClick={(e) => {
|
||||
setVisible(true);
|
||||
}}
|
||||
bordered={false}
|
||||
hoverable
|
||||
style={{ cursor: 'pointer', overflow: 'hidden' }}
|
||||
// bodyStyle={{ paddingBottom: 0 }}
|
||||
className={cls(
|
||||
css`
|
||||
.ant-card-body {
|
||||
padding: 16px;
|
||||
}
|
||||
.nb-row-divider {
|
||||
height: 16px;
|
||||
margin-top: -16px;
|
||||
&:last-child {
|
||||
margin-top: 0;
|
||||
return (
|
||||
<SchemaComponentOptions components={{}} scope={{}}>
|
||||
<Card
|
||||
onClick={(e) => {
|
||||
setVisible(true);
|
||||
}}
|
||||
bordered={false}
|
||||
hoverable
|
||||
style={{ cursor: 'pointer', overflow: 'hidden' }}
|
||||
// bodyStyle={{ paddingBottom: 0 }}
|
||||
className={cls(
|
||||
css`
|
||||
.ant-card-body {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
.ant-description-input {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ant-description-textarea {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ant-formily-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.nb-grid-row:last-of-type {
|
||||
.nb-grid-col {
|
||||
.nb-form-item:last-of-type {
|
||||
.ant-formily-item {
|
||||
margin-bottom: 0;
|
||||
.nb-row-divider {
|
||||
height: 16px;
|
||||
margin-top: -16px;
|
||||
&:last-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
.ant-description-input {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ant-description-textarea {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ant-formily-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.nb-grid-row:last-of-type {
|
||||
.nb-grid-col {
|
||||
.nb-form-item:last-of-type {
|
||||
.ant-formily-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
'kanban-no-label': labelDisabled,
|
||||
},
|
||||
`,
|
||||
{
|
||||
'kanban-no-label': labelDisabled,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<DndContext
|
||||
onDragStart={() => {
|
||||
setDisableCardDrag(true);
|
||||
}}
|
||||
onDragEnd={() => {
|
||||
setDisableCardDrag(false);
|
||||
}}
|
||||
>
|
||||
<FormLayout layout={'vertical'}>
|
||||
<RecursionField
|
||||
basePath={cardField.address.concat(`${columnIndex}.cards.${cardIndex}`)}
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</FormLayout>
|
||||
</DndContext>
|
||||
</Card>
|
||||
{cardViewerSchema && (
|
||||
<ActionContext.Provider
|
||||
value={{
|
||||
openMode: fieldSchema['x-component-props']?.['openMode'] || 'drawer',
|
||||
openSize: fieldSchema['x-component-props']?.['openSize'],
|
||||
visible,
|
||||
setVisible,
|
||||
}}
|
||||
>
|
||||
<RecordProvider record={card}>
|
||||
<RecursionField
|
||||
basePath={cardField.address.concat(`${columnIndex}.cardViewer.${cardIndex}`)}
|
||||
schema={cardViewerSchema}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</RecordProvider>
|
||||
</ActionContext.Provider>
|
||||
)}
|
||||
>
|
||||
<DndContext
|
||||
onDragStart={() => {
|
||||
setDisableCardDrag(true);
|
||||
}}
|
||||
onDragEnd={() => {
|
||||
setDisableCardDrag(false);
|
||||
}}
|
||||
>
|
||||
<FormLayout layout={'vertical'}>
|
||||
<RecursionField
|
||||
basePath={cardField.address.concat(`${columnIndex}.cards.${cardIndex}`)}
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</FormLayout>
|
||||
</DndContext>
|
||||
</Card>
|
||||
{cardViewerSchema && (
|
||||
<ActionContext.Provider
|
||||
value={{
|
||||
openMode: fieldSchema['x-component-props']?.['openMode'] || 'drawer',
|
||||
openSize: fieldSchema['x-component-props']?.['openSize'],
|
||||
visible,
|
||||
setVisible,
|
||||
}}
|
||||
>
|
||||
<RecordProvider record={card}>
|
||||
<RecursionField
|
||||
basePath={cardField.address.concat(`${columnIndex}.cardViewer.${cardIndex}`)}
|
||||
schema={cardViewerSchema}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</RecordProvider>
|
||||
</ActionContext.Provider>
|
||||
)}
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
});
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
},
|
||||
{ displayName: 'KanbanCard' },
|
||||
);
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { observer } from '@formily/react';
|
||||
|
||||
export const KanbanCardViewer: any = observer((props: any) => {
|
||||
return props.children;
|
||||
});
|
||||
export const KanbanCardViewer: any = observer(
|
||||
(props: any) => {
|
||||
return props.children;
|
||||
},
|
||||
{ displayName: 'KanbanCardViewer' },
|
||||
);
|
||||
|
@ -55,103 +55,106 @@ export const toColumns = (groupField: any, dataSource: Array<any> = []) => {
|
||||
return Object.values(columns);
|
||||
};
|
||||
|
||||
export const Kanban: any = observer((props: any) => {
|
||||
const { groupField, onCardDragEnd, ...restProps } = useProps(props);
|
||||
const field = useField<ArrayField>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
||||
const schemas = useMemo(
|
||||
() =>
|
||||
fieldSchema.reduceProperties(
|
||||
(buf, current) => {
|
||||
if (current['x-component'].endsWith('.Card')) {
|
||||
buf.card = current;
|
||||
} else if (current['x-component'].endsWith('.CardAdder')) {
|
||||
buf.cardAdder = current;
|
||||
} else if (current['x-component'].endsWith('.CardViewer')) {
|
||||
buf.cardViewer = current;
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
{ card: null, cardAdder: null, cardViewer: null },
|
||||
),
|
||||
[],
|
||||
);
|
||||
const handleCardRemove = (card, column) => {
|
||||
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
||||
field.value = updatedBoard.columns;
|
||||
};
|
||||
const handleCardDragEnd = (card, fromColumn, toColumn) => {
|
||||
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
||||
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
||||
field.value = updatedBoard.columns;
|
||||
};
|
||||
export const Kanban: any = observer(
|
||||
(props: any) => {
|
||||
const { groupField, onCardDragEnd, ...restProps } = useProps(props);
|
||||
const field = useField<ArrayField>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
||||
const schemas = useMemo(
|
||||
() =>
|
||||
fieldSchema.reduceProperties(
|
||||
(buf, current) => {
|
||||
if (current['x-component'].endsWith('.Card')) {
|
||||
buf.card = current;
|
||||
} else if (current['x-component'].endsWith('.CardAdder')) {
|
||||
buf.cardAdder = current;
|
||||
} else if (current['x-component'].endsWith('.CardViewer')) {
|
||||
buf.cardViewer = current;
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
{ card: null, cardAdder: null, cardViewer: null },
|
||||
),
|
||||
[],
|
||||
);
|
||||
const handleCardRemove = (card, column) => {
|
||||
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
||||
field.value = updatedBoard.columns;
|
||||
};
|
||||
const handleCardDragEnd = (card, fromColumn, toColumn) => {
|
||||
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
||||
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
||||
field.value = updatedBoard.columns;
|
||||
};
|
||||
|
||||
return (
|
||||
<Spin
|
||||
wrapperClassName={css`
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
> .ant-spin-container {
|
||||
return (
|
||||
<Spin
|
||||
wrapperClassName={css`
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
`}
|
||||
spinning={field.loading || false}
|
||||
>
|
||||
<Board
|
||||
{...restProps}
|
||||
allowAddCard={!!schemas.cardAdder}
|
||||
disableColumnDrag
|
||||
cardAdderPosition={'bottom'}
|
||||
disableCardDrag={restProps.disableCardDrag || disableCardDrag}
|
||||
onCardRemove={handleCardRemove}
|
||||
onCardDragEnd={handleCardDragEnd}
|
||||
renderColumnHeader={({ title, color }) => (
|
||||
<div className={'react-kanban-column-header'}>
|
||||
<Tag color={color}>{title}</Tag>
|
||||
</div>
|
||||
)}
|
||||
renderCard={(card, { column, dragging }) => {
|
||||
const columnIndex = field.value?.indexOf(column);
|
||||
const cardIndex = column?.cards?.indexOf(card);
|
||||
return (
|
||||
schemas.card && (
|
||||
<RecordProvider record={card}>
|
||||
<KanbanCardContext.Provider
|
||||
value={{
|
||||
setDisableCardDrag,
|
||||
cardViewerSchema: schemas.cardViewer,
|
||||
cardField: field,
|
||||
card,
|
||||
column,
|
||||
dragging,
|
||||
columnIndex,
|
||||
cardIndex,
|
||||
}}
|
||||
>
|
||||
<RecursionField name={schemas.card.name} schema={schemas.card} />
|
||||
</KanbanCardContext.Provider>
|
||||
</RecordProvider>
|
||||
)
|
||||
);
|
||||
}}
|
||||
renderCardAdder={({ column }) => {
|
||||
if (!schemas.cardAdder) {
|
||||
return null;
|
||||
> .ant-spin-container {
|
||||
height: 100%;
|
||||
}
|
||||
return (
|
||||
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
||||
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
||||
<RecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
||||
</SchemaComponentOptions>
|
||||
</KanbanColumnContext.Provider>
|
||||
);
|
||||
}}
|
||||
`}
|
||||
spinning={field.loading || false}
|
||||
>
|
||||
{{
|
||||
columns: field.value || [],
|
||||
}}
|
||||
</Board>
|
||||
</Spin>
|
||||
);
|
||||
});
|
||||
<Board
|
||||
{...restProps}
|
||||
allowAddCard={!!schemas.cardAdder}
|
||||
disableColumnDrag
|
||||
cardAdderPosition={'bottom'}
|
||||
disableCardDrag={restProps.disableCardDrag || disableCardDrag}
|
||||
onCardRemove={handleCardRemove}
|
||||
onCardDragEnd={handleCardDragEnd}
|
||||
renderColumnHeader={({ title, color }) => (
|
||||
<div className={'react-kanban-column-header'}>
|
||||
<Tag color={color}>{title}</Tag>
|
||||
</div>
|
||||
)}
|
||||
renderCard={(card, { column, dragging }) => {
|
||||
const columnIndex = field.value?.indexOf(column);
|
||||
const cardIndex = column?.cards?.indexOf(card);
|
||||
return (
|
||||
schemas.card && (
|
||||
<RecordProvider record={card}>
|
||||
<KanbanCardContext.Provider
|
||||
value={{
|
||||
setDisableCardDrag,
|
||||
cardViewerSchema: schemas.cardViewer,
|
||||
cardField: field,
|
||||
card,
|
||||
column,
|
||||
dragging,
|
||||
columnIndex,
|
||||
cardIndex,
|
||||
}}
|
||||
>
|
||||
<RecursionField name={schemas.card.name} schema={schemas.card} />
|
||||
</KanbanCardContext.Provider>
|
||||
</RecordProvider>
|
||||
)
|
||||
);
|
||||
}}
|
||||
renderCardAdder={({ column }) => {
|
||||
if (!schemas.cardAdder) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
||||
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
||||
<RecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
||||
</SchemaComponentOptions>
|
||||
</KanbanColumnContext.Provider>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{{
|
||||
columns: field.value || [],
|
||||
}}
|
||||
</Board>
|
||||
</Spin>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Kanban' },
|
||||
);
|
||||
|
@ -41,44 +41,47 @@ const MarkdownEditor = (props: any) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const MarkdownVoid: any = observer((props: any) => {
|
||||
const { content, className } = props;
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { onSave, onCancel } = props;
|
||||
const { html, loading } = useParseMarkdown(content);
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return field?.editable ? (
|
||||
<MarkdownEditor
|
||||
{...props}
|
||||
className
|
||||
defaultValue={content}
|
||||
onCancel={() => {
|
||||
field.editable = false;
|
||||
onCancel?.();
|
||||
}}
|
||||
onSubmit={async (value) => {
|
||||
field.editable = false;
|
||||
schema['x-component-props'] ?? (schema['x-component-props'] = {});
|
||||
schema['x-component-props']['content'] = value;
|
||||
field.componentProps.content = value;
|
||||
onSave?.(schema);
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
'x-uid': schema['x-uid'],
|
||||
'x-component-props': {
|
||||
content: value,
|
||||
export const MarkdownVoid: any = observer(
|
||||
(props: any) => {
|
||||
const { content, className } = props;
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { onSave, onCancel } = props;
|
||||
const { html, loading } = useParseMarkdown(content);
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return field?.editable ? (
|
||||
<MarkdownEditor
|
||||
{...props}
|
||||
className
|
||||
defaultValue={content}
|
||||
onCancel={() => {
|
||||
field.editable = false;
|
||||
onCancel?.();
|
||||
}}
|
||||
onSubmit={async (value) => {
|
||||
field.editable = false;
|
||||
schema['x-component-props'] ?? (schema['x-component-props'] = {});
|
||||
schema['x-component-props']['content'] = value;
|
||||
field.componentProps.content = value;
|
||||
onSave?.(schema);
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
'x-uid': schema['x-uid'],
|
||||
'x-component-props': {
|
||||
content: value,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={cls(['nb-markdown', className])} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
);
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={cls(['nb-markdown', className])} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
);
|
||||
},
|
||||
{ displayName: 'MarkdownVoid' },
|
||||
);
|
||||
|
||||
MarkdownVoid.Designer = MarkdownVoidDesigner;
|
||||
|
@ -23,24 +23,27 @@ const schema = {
|
||||
},
|
||||
};
|
||||
|
||||
const Editable = observer((props: any) => {
|
||||
const filed = useField<any>();
|
||||
if (filed.editable) {
|
||||
return props.children;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
filed.editable = true;
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<div>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const Editable = observer(
|
||||
(props: any) => {
|
||||
const filed = useField<any>();
|
||||
if (filed.editable) {
|
||||
return props.children;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
filed.editable = true;
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<div>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Editable' },
|
||||
);
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
|
@ -136,148 +136,149 @@ const useSideMenuRef = () => {
|
||||
|
||||
const MenuItemDesignerContext = createContext(null);
|
||||
|
||||
export const Menu: ComposedMenu = observer((props) => {
|
||||
const {
|
||||
onSelect,
|
||||
mode,
|
||||
selectedUid,
|
||||
defaultSelectedUid,
|
||||
sideMenuRefScopeKey,
|
||||
defaultSelectedKeys: dSelectedKeys,
|
||||
defaultOpenKeys: dOpenKeys,
|
||||
...others
|
||||
} = useProps(props);
|
||||
const { t } = useTranslation();
|
||||
const Designer = useDesigner();
|
||||
const schema = useFieldSchema();
|
||||
const { refresh } = useDesignable();
|
||||
const api = useAPIClient();
|
||||
const { render } = useSchemaInitializer(schema['x-initializer']);
|
||||
const sideMenuRef = useSideMenuRef();
|
||||
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
||||
if (dSelectedKeys) {
|
||||
return dSelectedKeys;
|
||||
}
|
||||
if (defaultSelectedUid) {
|
||||
return findKeysByUid(schema, defaultSelectedUid);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [defaultOpenKeys, setDefaultOpenKeys] = useState(() => {
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
return dOpenKeys || defaultSelectedKeys;
|
||||
}
|
||||
return dOpenKeys;
|
||||
});
|
||||
const [sideMenuSchema, setSideMenuSchema] = useState<Schema>(() => {
|
||||
const key = defaultSelectedKeys?.[0] || null;
|
||||
if (mode === 'mix' && key) {
|
||||
const s = schema.properties?.[key];
|
||||
if (s['x-component'] === 'Menu.SubMenu') {
|
||||
return s;
|
||||
export const Menu: ComposedMenu = observer(
|
||||
(props) => {
|
||||
const {
|
||||
onSelect,
|
||||
mode,
|
||||
selectedUid,
|
||||
defaultSelectedUid,
|
||||
sideMenuRefScopeKey,
|
||||
defaultSelectedKeys: dSelectedKeys,
|
||||
defaultOpenKeys: dOpenKeys,
|
||||
...others
|
||||
} = useProps(props);
|
||||
const { t } = useTranslation();
|
||||
const Designer = useDesigner();
|
||||
const schema = useFieldSchema();
|
||||
const { refresh } = useDesignable();
|
||||
const api = useAPIClient();
|
||||
const { render } = useSchemaInitializer(schema['x-initializer']);
|
||||
const sideMenuRef = useSideMenuRef();
|
||||
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
||||
if (dSelectedKeys) {
|
||||
return dSelectedKeys;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
useEffect(() => {
|
||||
const keys = findKeysByUid(schema, selectedUid);
|
||||
setDefaultSelectedKeys(keys);
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
setDefaultOpenKeys(dOpenKeys || keys);
|
||||
}
|
||||
const key = keys?.[0] || null;
|
||||
if (mode === 'mix') {
|
||||
if (key) {
|
||||
if (defaultSelectedUid) {
|
||||
return findKeysByUid(schema, defaultSelectedUid);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [defaultOpenKeys, setDefaultOpenKeys] = useState(() => {
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
return dOpenKeys || defaultSelectedKeys;
|
||||
}
|
||||
return dOpenKeys;
|
||||
});
|
||||
const [sideMenuSchema, setSideMenuSchema] = useState<Schema>(() => {
|
||||
const key = defaultSelectedKeys?.[0] || null;
|
||||
if (mode === 'mix' && key) {
|
||||
const s = schema.properties?.[key];
|
||||
if (s['x-component'] === 'Menu.SubMenu') {
|
||||
setSideMenuSchema(s);
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
setSideMenuSchema(null);
|
||||
}
|
||||
}
|
||||
}, [selectedUid]);
|
||||
useEffect(() => {
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
setDefaultOpenKeys(defaultSelectedKeys);
|
||||
}
|
||||
}, [defaultSelectedKeys]);
|
||||
useEffect(() => {
|
||||
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
||||
if (!sideMenuElement) {
|
||||
return;
|
||||
}
|
||||
sideMenuElement.style.display = sideMenuSchema?.['x-component'] === 'Menu.SubMenu' ? 'block' : 'none';
|
||||
}, [sideMenuSchema?.name, sideMenuRef]);
|
||||
const { designable } = useDesignable();
|
||||
return (
|
||||
<DndContext>
|
||||
<MenuItemDesignerContext.Provider value={Designer}>
|
||||
<MenuModeContext.Provider value={mode}>
|
||||
<AntdMenu
|
||||
{...others}
|
||||
style={
|
||||
{
|
||||
// width: mode === 'mix' ? '100%' : undefined,
|
||||
}
|
||||
}
|
||||
className={css`
|
||||
.ant-menu-item:hover {
|
||||
> .ant-menu-title-content > div {
|
||||
.general-schema-designer {
|
||||
display: block;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
useEffect(() => {
|
||||
const keys = findKeysByUid(schema, selectedUid);
|
||||
setDefaultSelectedKeys(keys);
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
setDefaultOpenKeys(dOpenKeys || keys);
|
||||
}
|
||||
const key = keys?.[0] || null;
|
||||
if (mode === 'mix') {
|
||||
if (key) {
|
||||
const s = schema.properties?.[key];
|
||||
if (s['x-component'] === 'Menu.SubMenu') {
|
||||
setSideMenuSchema(s);
|
||||
}
|
||||
} else {
|
||||
setSideMenuSchema(null);
|
||||
}
|
||||
}
|
||||
}, [selectedUid]);
|
||||
useEffect(() => {
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
setDefaultOpenKeys(defaultSelectedKeys);
|
||||
}
|
||||
}, [defaultSelectedKeys]);
|
||||
useEffect(() => {
|
||||
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
||||
if (!sideMenuElement) {
|
||||
return;
|
||||
}
|
||||
sideMenuElement.style.display = sideMenuSchema?.['x-component'] === 'Menu.SubMenu' ? 'block' : 'none';
|
||||
}, [sideMenuSchema?.name, sideMenuRef]);
|
||||
const { designable } = useDesignable();
|
||||
return (
|
||||
<DndContext>
|
||||
<MenuItemDesignerContext.Provider value={Designer}>
|
||||
<MenuModeContext.Provider value={mode}>
|
||||
<AntdMenu
|
||||
{...others}
|
||||
style={
|
||||
{
|
||||
// width: mode === 'mix' ? '100%' : undefined,
|
||||
}
|
||||
}
|
||||
`}
|
||||
onSelect={(info: any) => {
|
||||
const s = schema.properties[info.key];
|
||||
if (mode === 'mix') {
|
||||
setSideMenuSchema(s);
|
||||
if (s['x-component'] !== 'Menu.SubMenu') {
|
||||
onSelect && onSelect(info);
|
||||
} else {
|
||||
const menuItemSchema = findMenuItem(s);
|
||||
if (!menuItemSchema) {
|
||||
return;
|
||||
className={css`
|
||||
.ant-menu-item:hover {
|
||||
> .ant-menu-title-content > div {
|
||||
.general-schema-designer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
setLoading(true);
|
||||
const keys = findKeysByUid(schema, menuItemSchema['x-uid']);
|
||||
setDefaultSelectedKeys(keys);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 100);
|
||||
onSelect &&
|
||||
onSelect({
|
||||
key: menuItemSchema.name,
|
||||
item: {
|
||||
props: {
|
||||
schema: menuItemSchema,
|
||||
}
|
||||
`}
|
||||
onSelect={(info: any) => {
|
||||
const s = schema.properties[info.key];
|
||||
if (mode === 'mix') {
|
||||
setSideMenuSchema(s);
|
||||
if (s['x-component'] !== 'Menu.SubMenu') {
|
||||
onSelect && onSelect(info);
|
||||
} else {
|
||||
const menuItemSchema = findMenuItem(s);
|
||||
if (!menuItemSchema) {
|
||||
return;
|
||||
}
|
||||
// TODO
|
||||
setLoading(true);
|
||||
const keys = findKeysByUid(schema, menuItemSchema['x-uid']);
|
||||
setDefaultSelectedKeys(keys);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 100);
|
||||
onSelect &&
|
||||
onSelect({
|
||||
key: menuItemSchema.name,
|
||||
item: {
|
||||
props: {
|
||||
schema: menuItemSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
onSelect && onSelect(info);
|
||||
}
|
||||
} else {
|
||||
onSelect && onSelect(info);
|
||||
}
|
||||
}}
|
||||
mode={mode === 'mix' ? 'horizontal' : mode}
|
||||
defaultOpenKeys={defaultOpenKeys}
|
||||
defaultSelectedKeys={defaultSelectedKeys}
|
||||
selectedKeys={defaultSelectedKeys}
|
||||
>
|
||||
{designable && (
|
||||
<AntdMenu.Item disabled key="x-designer-button" style={{ padding: '0 8px', order: 9999 }}>
|
||||
{render({ style: { background: 'none' } })}
|
||||
</AntdMenu.Item>
|
||||
)}
|
||||
{props.children}
|
||||
</AntdMenu>
|
||||
{loading
|
||||
? null
|
||||
: mode === 'mix' &&
|
||||
}}
|
||||
mode={mode === 'mix' ? 'horizontal' : mode}
|
||||
defaultOpenKeys={defaultOpenKeys}
|
||||
defaultSelectedKeys={defaultSelectedKeys}
|
||||
selectedKeys={defaultSelectedKeys}
|
||||
>
|
||||
{designable && (
|
||||
<AntdMenu.Item disabled key="x-designer-button" style={{ padding: '0 8px', order: 9999 }}>
|
||||
{render({ style: { background: 'none' } })}
|
||||
</AntdMenu.Item>
|
||||
)}
|
||||
{props.children}
|
||||
</AntdMenu>
|
||||
{loading
|
||||
? null
|
||||
: mode === 'mix' &&
|
||||
sideMenuSchema?.['x-component'] === 'Menu.SubMenu' &&
|
||||
sideMenuRef?.current?.firstChild &&
|
||||
createPortal(
|
||||
@ -290,37 +291,37 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
onSelect && onSelect(info);
|
||||
}}
|
||||
className={css`
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
.ant-menu-item {
|
||||
> .ant-menu-title-content {
|
||||
margin-left: -24px;
|
||||
margin-right: -16px;
|
||||
padding: 0 16px 0 24px;
|
||||
> div {
|
||||
> .general-schema-designer {
|
||||
right: 6px !important;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
.ant-menu-item {
|
||||
> .ant-menu-title-content {
|
||||
margin-left: -24px;
|
||||
margin-right: -16px;
|
||||
padding: 0 16px 0 24px;
|
||||
> div {
|
||||
> .general-schema-designer {
|
||||
right: 6px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-menu-submenu-title {
|
||||
.ant-menu-title-content {
|
||||
margin-left: -24px;
|
||||
margin-right: -34px;
|
||||
padding: 0 34px 0 24px;
|
||||
> div {
|
||||
> .general-schema-designer {
|
||||
right: 6px !important;
|
||||
}
|
||||
> span.anticon {
|
||||
margin-right: 10px;
|
||||
.ant-menu-submenu-title {
|
||||
.ant-menu-title-content {
|
||||
margin-left: -24px;
|
||||
margin-right: -34px;
|
||||
padding: 0 34px 0 24px;
|
||||
> div {
|
||||
> .general-schema-designer {
|
||||
right: 6px !important;
|
||||
}
|
||||
> span.anticon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`}
|
||||
>
|
||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
||||
{render({
|
||||
@ -340,117 +341,128 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
</MenuModeContext.Provider>,
|
||||
sideMenuRef.current.firstChild,
|
||||
)}
|
||||
</MenuModeContext.Provider>
|
||||
</MenuItemDesignerContext.Provider>
|
||||
</DndContext>
|
||||
);
|
||||
});
|
||||
</MenuModeContext.Provider>
|
||||
</MenuItemDesignerContext.Provider>
|
||||
</DndContext>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Menu' },
|
||||
);
|
||||
|
||||
Menu.Item = observer((props) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
return (
|
||||
<AntdMenu.Item
|
||||
{...others}
|
||||
className={css`
|
||||
:active {
|
||||
background: inherit;
|
||||
}
|
||||
`}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
schema={schema}
|
||||
>
|
||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||
<Icon type={icon} />
|
||||
<span
|
||||
className={css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
`}
|
||||
>
|
||||
{field.title}
|
||||
</span>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</AntdMenu.Item>
|
||||
);
|
||||
});
|
||||
|
||||
Menu.URL = observer((props) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
return (
|
||||
<AntdMenu.Item
|
||||
{...others}
|
||||
className={css`
|
||||
:active {
|
||||
background: inherit;
|
||||
}
|
||||
`}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
schema={schema}
|
||||
onClick={() => {
|
||||
window.open(props.href, '_blank');
|
||||
}}
|
||||
>
|
||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||
<Icon type={icon} />
|
||||
<span
|
||||
className={css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
`}
|
||||
>
|
||||
{field.title}
|
||||
</span>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</AntdMenu.Item>
|
||||
);
|
||||
});
|
||||
|
||||
Menu.SubMenu = observer((props) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const mode = useContext(MenuModeContext);
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
if (mode === 'mix') {
|
||||
return <Menu.Item {...props} />;
|
||||
}
|
||||
return (
|
||||
<AntdMenu.SubMenu
|
||||
{...others}
|
||||
className={css`
|
||||
:active {
|
||||
background: inherit;
|
||||
}
|
||||
`}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
title={
|
||||
<SortableItem className={subMenuDesignerCss} removeParentsIfNoChildren={false}>
|
||||
Menu.Item = observer(
|
||||
(props) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
return (
|
||||
<AntdMenu.Item
|
||||
{...others}
|
||||
className={css`
|
||||
:active {
|
||||
background: inherit;
|
||||
}
|
||||
`}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
schema={schema}
|
||||
>
|
||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||
<Icon type={icon} />
|
||||
{field.title}
|
||||
<span
|
||||
className={css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
`}
|
||||
>
|
||||
{field.title}
|
||||
</span>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
}
|
||||
>
|
||||
<RecursionField schema={schema} onlyRenderProperties />
|
||||
</AntdMenu.SubMenu>
|
||||
);
|
||||
});
|
||||
</AntdMenu.Item>
|
||||
);
|
||||
},
|
||||
{ displayName: 'MenuItem' },
|
||||
);
|
||||
|
||||
Menu.URL = observer(
|
||||
(props) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
return (
|
||||
<AntdMenu.Item
|
||||
{...others}
|
||||
className={css`
|
||||
:active {
|
||||
background: inherit;
|
||||
}
|
||||
`}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
schema={schema}
|
||||
onClick={() => {
|
||||
window.open(props.href, '_blank');
|
||||
}}
|
||||
>
|
||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||
<Icon type={icon} />
|
||||
<span
|
||||
className={css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
`}
|
||||
>
|
||||
{field.title}
|
||||
</span>
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
</AntdMenu.Item>
|
||||
);
|
||||
},
|
||||
{ displayName: 'MenuURL' },
|
||||
);
|
||||
|
||||
Menu.SubMenu = observer(
|
||||
(props) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
const mode = useContext(MenuModeContext);
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
if (mode === 'mix') {
|
||||
return <Menu.Item {...props} />;
|
||||
}
|
||||
return (
|
||||
<AntdMenu.SubMenu
|
||||
{...others}
|
||||
className={css`
|
||||
:active {
|
||||
background: inherit;
|
||||
}
|
||||
`}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
title={
|
||||
<SortableItem className={subMenuDesignerCss} removeParentsIfNoChildren={false}>
|
||||
<Icon type={icon} />
|
||||
{field.title}
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
}
|
||||
>
|
||||
<RecursionField schema={schema} onlyRenderProperties />
|
||||
</AntdMenu.SubMenu>
|
||||
);
|
||||
},
|
||||
{ displayName: 'MenuSubMenu' },
|
||||
);
|
||||
|
||||
Menu.Designer = MenuDesigner;
|
||||
|
@ -3,20 +3,23 @@ import { Pagination as AntdPagination } from 'antd';
|
||||
import React, { KeyboardEventHandler } from 'react';
|
||||
import { useProps } from '../../hooks/useProps';
|
||||
|
||||
export const Pagination = observer((props: any) => {
|
||||
const { hidden, ...others } = useProps(props);
|
||||
if (hidden) {
|
||||
return null;
|
||||
}
|
||||
const onKeypress: KeyboardEventHandler<HTMLDivElement> = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
export const Pagination = observer(
|
||||
(props: any) => {
|
||||
const { hidden, ...others } = useProps(props);
|
||||
if (hidden) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const onKeypress: KeyboardEventHandler<HTMLDivElement> = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div onKeyPress={onKeypress}>
|
||||
<AntdPagination {...others} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div onKeyPress={onKeypress}>
|
||||
<AntdPagination {...others} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Pagination' },
|
||||
);
|
||||
|
@ -25,98 +25,101 @@ const toValue = (value, placeholder) => {
|
||||
return value;
|
||||
};
|
||||
|
||||
export const ReadPrettyRecordPicker: React.FC = observer((props: any) => {
|
||||
const { ellipsis } = props;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const recordCtx = useRecord();
|
||||
const { getCollectionJoinField } = useCollectionManager();
|
||||
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
||||
// const field = useField<Field>();
|
||||
const fieldNames = useFieldNames(props);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { getField } = useCollection();
|
||||
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||
const [record, setRecord] = useState({});
|
||||
const compile = useCompile();
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const showFilePicker = isShowFilePicker(labelUiSchema);
|
||||
const { snapshot } = useActionContext();
|
||||
const isTagsMode = fieldSchema['x-component-props']?.mode === 'tags';
|
||||
export const ReadPrettyRecordPicker: React.FC = observer(
|
||||
(props: any) => {
|
||||
const { ellipsis } = props;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const recordCtx = useRecord();
|
||||
const { getCollectionJoinField } = useCollectionManager();
|
||||
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
||||
// const field = useField<Field>();
|
||||
const fieldNames = useFieldNames(props);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { getField } = useCollection();
|
||||
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||
const [record, setRecord] = useState({});
|
||||
const compile = useCompile();
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const showFilePicker = isShowFilePicker(labelUiSchema);
|
||||
const { snapshot } = useActionContext();
|
||||
const isTagsMode = fieldSchema['x-component-props']?.mode === 'tags';
|
||||
|
||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||
|
||||
if (showFilePicker) {
|
||||
return collectionField ? <Preview {...props} /> : null;
|
||||
}
|
||||
if (showFilePicker) {
|
||||
return collectionField ? <Preview {...props} /> : null;
|
||||
}
|
||||
|
||||
const renderRecords = () =>
|
||||
toArr(props.value).map((record, index, arr) => {
|
||||
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
||||
const text = getLabelFormatValue(labelUiSchema, val, true);
|
||||
return (
|
||||
<Fragment key={`${record.id}_${index}`}>
|
||||
<span>
|
||||
{snapshot || isTagsMode ? (
|
||||
text
|
||||
) : (
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setVisible(true);
|
||||
setRecord(record);
|
||||
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
)}
|
||||
</span>
|
||||
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
const renderRecords = () =>
|
||||
toArr(props.value).map((record, index, arr) => {
|
||||
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
||||
const text = getLabelFormatValue(labelUiSchema, val, true);
|
||||
return (
|
||||
<Fragment key={`${record.id}_${index}`}>
|
||||
<span>
|
||||
{snapshot || isTagsMode ? (
|
||||
text
|
||||
) : (
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setVisible(true);
|
||||
setRecord(record);
|
||||
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
)}
|
||||
</span>
|
||||
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
const renderWithoutTableFieldResourceProvider = () => (
|
||||
<WithoutTableFieldResource.Provider value={true}>
|
||||
<FormProvider>
|
||||
<RecursionField
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'RecordPicker.Viewer';
|
||||
}}
|
||||
/>
|
||||
</FormProvider>
|
||||
</WithoutTableFieldResource.Provider>
|
||||
);
|
||||
|
||||
const renderRecordProvider = () => {
|
||||
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||
|
||||
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
</RecordProvider>
|
||||
) : (
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
const renderWithoutTableFieldResourceProvider = () => (
|
||||
<WithoutTableFieldResource.Provider value={true}>
|
||||
<FormProvider>
|
||||
<RecursionField
|
||||
schema={fieldSchema}
|
||||
onlyRenderProperties
|
||||
filterProperties={(s) => {
|
||||
return s['x-component'] === 'RecordPicker.Viewer';
|
||||
}}
|
||||
/>
|
||||
</FormProvider>
|
||||
</WithoutTableFieldResource.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
return collectionField ? (
|
||||
<div>
|
||||
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
|
||||
<CollectionProvider name={collectionField.target ?? collectionField.targetCollection}>
|
||||
<EllipsisWithTooltip ellipsis={ellipsis} ref={ellipsisWithTooltipRef}>
|
||||
{renderRecords()}
|
||||
</EllipsisWithTooltip>
|
||||
<ActionContext.Provider
|
||||
value={{ visible, setVisible, openMode: 'drawer', snapshot: collectionField.interface === 'snapshot' }}
|
||||
>
|
||||
{renderRecordProvider()}
|
||||
</ActionContext.Provider>
|
||||
</CollectionProvider>
|
||||
</BlockAssociationContext.Provider>
|
||||
</div>
|
||||
) : null;
|
||||
});
|
||||
const renderRecordProvider = () => {
|
||||
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||
|
||||
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
</RecordProvider>
|
||||
) : (
|
||||
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||
);
|
||||
};
|
||||
|
||||
return collectionField ? (
|
||||
<div>
|
||||
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
|
||||
<CollectionProvider name={collectionField.target ?? collectionField.targetCollection}>
|
||||
<EllipsisWithTooltip ellipsis={ellipsis} ref={ellipsisWithTooltipRef}>
|
||||
{renderRecords()}
|
||||
</EllipsisWithTooltip>
|
||||
<ActionContext.Provider
|
||||
value={{ visible, setVisible, openMode: 'drawer', snapshot: collectionField.interface === 'snapshot' }}
|
||||
>
|
||||
{renderRecordProvider()}
|
||||
</ActionContext.Provider>
|
||||
</CollectionProvider>
|
||||
</BlockAssociationContext.Provider>
|
||||
</div>
|
||||
) : null;
|
||||
},
|
||||
{ displayName: 'ReadPrettyRecordPicker' },
|
||||
);
|
||||
|
@ -6,19 +6,20 @@ import { useRequest } from '../../../api-client';
|
||||
import { useRecord } from '../../../record-provider';
|
||||
import { useActionContext } from '../action';
|
||||
|
||||
export const ReadPretty = observer((props: any) => {
|
||||
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
||||
const field = useField<any>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const record = useRecord();
|
||||
const { snapshot } = useActionContext();
|
||||
export const ReadPretty = observer(
|
||||
(props: any) => {
|
||||
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
||||
const field = useField<any>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const record = useRecord();
|
||||
const { snapshot } = useActionContext();
|
||||
|
||||
const { data } = useRequest(
|
||||
snapshot
|
||||
? async () => ({
|
||||
const { data } = useRequest(
|
||||
snapshot
|
||||
? async () => ({
|
||||
data: record[fieldSchema.name],
|
||||
})
|
||||
: {
|
||||
: {
|
||||
action: 'list',
|
||||
...props.service,
|
||||
params: {
|
||||
@ -30,10 +31,12 @@ export const ReadPretty = observer((props: any) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
refreshDeps: [props.service, field.value],
|
||||
},
|
||||
);
|
||||
{
|
||||
refreshDeps: [props.service, field.value],
|
||||
},
|
||||
);
|
||||
|
||||
return <Select.ReadPretty {...props} options={data?.data}></Select.ReadPretty>;
|
||||
});
|
||||
return <Select.ReadPretty {...props} options={data?.data}></Select.ReadPretty>;
|
||||
},
|
||||
{ displayName: 'ReadPretty' },
|
||||
);
|
||||
|
@ -12,29 +12,32 @@ type Composed = {
|
||||
Object?: React.FC<any>;
|
||||
};
|
||||
|
||||
export const ReadPretty = observer((props: any) => {
|
||||
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
||||
const field = useField<any>();
|
||||
const compile = useCompile();
|
||||
export const ReadPretty = observer(
|
||||
(props: any) => {
|
||||
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
||||
const field = useField<any>();
|
||||
const compile = useCompile();
|
||||
|
||||
if (!isValid(props.value)) {
|
||||
return <div />;
|
||||
}
|
||||
if (isArrayField(field) && field?.value?.length === 0) {
|
||||
return <div />;
|
||||
}
|
||||
const dataSource = field.dataSource || props.options || [];
|
||||
const options = getCurrentOptions(field.value, dataSource, fieldNames);
|
||||
if (!isValid(props.value)) {
|
||||
return <div />;
|
||||
}
|
||||
if (isArrayField(field) && field?.value?.length === 0) {
|
||||
return <div />;
|
||||
}
|
||||
const dataSource = field.dataSource || props.options || [];
|
||||
const options = getCurrentOptions(field.value, dataSource, fieldNames);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EllipsisWithTooltip ellipsis={props.ellipsis}>
|
||||
{options.map((option, key) => (
|
||||
<Tag key={key} color={option[fieldNames.color]} icon={option.icon}>
|
||||
{compile(option[fieldNames.label])}
|
||||
</Tag>
|
||||
))}
|
||||
</EllipsisWithTooltip>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<EllipsisWithTooltip ellipsis={props.ellipsis}>
|
||||
{options.map((option, key) => (
|
||||
<Tag key={key} color={option[fieldNames.color]} icon={option.icon}>
|
||||
{compile(option[fieldNames.label])}
|
||||
</Tag>
|
||||
))}
|
||||
</EllipsisWithTooltip>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'ReadPretty' },
|
||||
);
|
||||
|
@ -42,12 +42,15 @@ export const designerCss = css`
|
||||
}
|
||||
`;
|
||||
|
||||
export const TableColumnActionBar = observer((props) => {
|
||||
const Designer = useDesigner();
|
||||
return (
|
||||
<SortableItem className={designerCss}>
|
||||
<Designer />
|
||||
{props.children}
|
||||
</SortableItem>
|
||||
);
|
||||
});
|
||||
export const TableColumnActionBar = observer(
|
||||
(props) => {
|
||||
const Designer = useDesigner();
|
||||
return (
|
||||
<SortableItem className={designerCss}>
|
||||
<Designer />
|
||||
{props.children}
|
||||
</SortableItem>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TableColumnActionBar' },
|
||||
);
|
||||
|
@ -163,164 +163,165 @@ const useValidator = (validator: (value: any) => string) => {
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const Table: any = observer((props: any) => {
|
||||
const { pagination: pagination1, useProps, onChange, ...others1 } = props;
|
||||
const { pagination: pagination2, onClickRow, ...others2 } = useProps?.() || {};
|
||||
const {
|
||||
dragSort = false,
|
||||
showIndex = true,
|
||||
onRowSelectionChange,
|
||||
onChange: onTableChange,
|
||||
rowSelection,
|
||||
rowKey,
|
||||
required,
|
||||
onExpand,
|
||||
...others
|
||||
} = { ...others1, ...others2 } as any;
|
||||
const field = useArrayField(others);
|
||||
const columns = useTableColumns(others);
|
||||
const schema = useFieldSchema();
|
||||
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
||||
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
||||
const { expandFlag } = ctx;
|
||||
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => {}));
|
||||
const paginationProps = usePaginationProps(pagination1, pagination2);
|
||||
const requiredValidator = field.required || required;
|
||||
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
||||
const [expandedKeys, setExpandesKeys] = useState([]);
|
||||
const [allIncludesChildren, setAllIncludesChildren] = useState([]);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>(field?.data?.selectedRowKeys || []);
|
||||
const [selectedRow, setSelectedRow] = useState([]);
|
||||
export const Table: any = observer(
|
||||
(props: any) => {
|
||||
const { pagination: pagination1, useProps, onChange, ...others1 } = props;
|
||||
const { pagination: pagination2, onClickRow, ...others2 } = useProps?.() || {};
|
||||
const {
|
||||
dragSort = false,
|
||||
showIndex = true,
|
||||
onRowSelectionChange,
|
||||
onChange: onTableChange,
|
||||
rowSelection,
|
||||
rowKey,
|
||||
required,
|
||||
onExpand,
|
||||
...others
|
||||
} = { ...others1, ...others2 } as any;
|
||||
const field = useArrayField(others);
|
||||
const columns = useTableColumns(others);
|
||||
const schema = useFieldSchema();
|
||||
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
||||
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
||||
const { expandFlag } = ctx;
|
||||
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => { }));
|
||||
const paginationProps = usePaginationProps(pagination1, pagination2);
|
||||
const requiredValidator = field.required || required;
|
||||
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
||||
const [expandedKeys, setExpandesKeys] = useState([]);
|
||||
const [allIncludesChildren, setAllIncludesChildren] = useState([]);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>(field?.data?.selectedRowKeys || []);
|
||||
const [selectedRow, setSelectedRow] = useState([]);
|
||||
|
||||
let onRow = null,
|
||||
highlightRow = '';
|
||||
let onRow = null,
|
||||
highlightRow = '';
|
||||
|
||||
if (onClickRow) {
|
||||
onRow = (record) => {
|
||||
return {
|
||||
onClick: () => onClickRow(record, setSelectedRow, selectedRow),
|
||||
if (onClickRow) {
|
||||
onRow = (record) => {
|
||||
return {
|
||||
onClick: () => onClickRow(record, setSelectedRow, selectedRow),
|
||||
};
|
||||
};
|
||||
};
|
||||
highlightRow = css`
|
||||
& > td {
|
||||
background-color: #caedff !important;
|
||||
}
|
||||
&:hover > td {
|
||||
background-color: #caedff !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
field.setValidator((value) => {
|
||||
if (requiredValidator) {
|
||||
return Array.isArray(value) && value.length > 0 ? null : 'The field value is required';
|
||||
}
|
||||
return;
|
||||
});
|
||||
}, [requiredValidator]);
|
||||
|
||||
useEffect(() => {
|
||||
if (treeTable !== false) {
|
||||
const keys = getIdsWithChildren(field.value?.slice?.());
|
||||
setAllIncludesChildren(keys);
|
||||
highlightRow = css`
|
||||
& > td {
|
||||
background-color: #caedff !important;
|
||||
}
|
||||
&:hover > td {
|
||||
background-color: #caedff !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}, [field.value]);
|
||||
useEffect(() => {
|
||||
if (expandFlag) {
|
||||
setExpandesKeys(allIncludesChildren);
|
||||
} else {
|
||||
setExpandesKeys([]);
|
||||
}
|
||||
}, [expandFlag, allIncludesChildren]);
|
||||
|
||||
const components = useMemo(() => {
|
||||
return {
|
||||
header: {
|
||||
wrapper: (props) => {
|
||||
return (
|
||||
<DndContext>
|
||||
<thead {...props} />
|
||||
</DndContext>
|
||||
);
|
||||
useEffect(() => {
|
||||
field.setValidator((value) => {
|
||||
if (requiredValidator) {
|
||||
return Array.isArray(value) && value.length > 0 ? null : 'The field value is required';
|
||||
}
|
||||
return;
|
||||
});
|
||||
}, [requiredValidator]);
|
||||
|
||||
useEffect(() => {
|
||||
if (treeTable !== false) {
|
||||
const keys = getIdsWithChildren(field.value?.slice?.());
|
||||
setAllIncludesChildren(keys);
|
||||
}
|
||||
}, [field.value]);
|
||||
useEffect(() => {
|
||||
if (expandFlag) {
|
||||
setExpandesKeys(allIncludesChildren);
|
||||
} else {
|
||||
setExpandesKeys([]);
|
||||
}
|
||||
}, [expandFlag, allIncludesChildren]);
|
||||
|
||||
const components = useMemo(() => {
|
||||
return {
|
||||
header: {
|
||||
wrapper: (props) => {
|
||||
return (
|
||||
<DndContext>
|
||||
<thead {...props} />
|
||||
</DndContext>
|
||||
);
|
||||
},
|
||||
cell: (props) => {
|
||||
return (
|
||||
<th
|
||||
{...props}
|
||||
className={cls(
|
||||
props.className,
|
||||
css`
|
||||
max-width: 300px;
|
||||
white-space: nowrap;
|
||||
&:hover .general-schema-designer {
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
cell: (props) => {
|
||||
return (
|
||||
<th
|
||||
body: {
|
||||
wrapper: (props) => {
|
||||
return (
|
||||
<DndContext
|
||||
onDragEnd={(e) => {
|
||||
if (!e.active || !e.over) {
|
||||
console.warn('move cancel');
|
||||
return;
|
||||
}
|
||||
|
||||
const fromIndex = e.active?.data.current?.sortable?.index;
|
||||
const toIndex = e.over?.data.current?.sortable?.index;
|
||||
const from = field.value[fromIndex];
|
||||
const to = field.value[toIndex];
|
||||
field.move(fromIndex, toIndex);
|
||||
onRowDragEnd({ fromIndex, toIndex, from, to });
|
||||
}}
|
||||
>
|
||||
<tbody {...props} />
|
||||
</DndContext>
|
||||
);
|
||||
},
|
||||
row: (props) => {
|
||||
return <SortableRow {...props}></SortableRow>;
|
||||
},
|
||||
cell: (props) => (
|
||||
<td
|
||||
{...props}
|
||||
className={cls(
|
||||
className={classNames(
|
||||
props.className,
|
||||
css`
|
||||
max-width: 300px;
|
||||
white-space: nowrap;
|
||||
&:hover .general-schema-designer {
|
||||
display: block;
|
||||
.nb-read-pretty-input-number {
|
||||
text-align: right;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
),
|
||||
},
|
||||
},
|
||||
body: {
|
||||
wrapper: (props) => {
|
||||
return (
|
||||
<DndContext
|
||||
onDragEnd={(e) => {
|
||||
if (!e.active || !e.over) {
|
||||
console.warn('move cancel');
|
||||
return;
|
||||
}
|
||||
};
|
||||
}, [field, onRowDragEnd, dragSort]);
|
||||
|
||||
const fromIndex = e.active?.data.current?.sortable?.index;
|
||||
const toIndex = e.over?.data.current?.sortable?.index;
|
||||
const from = field.value[fromIndex];
|
||||
const to = field.value[toIndex];
|
||||
field.move(fromIndex, toIndex);
|
||||
onRowDragEnd({ fromIndex, toIndex, from, to });
|
||||
}}
|
||||
>
|
||||
<tbody {...props} />
|
||||
</DndContext>
|
||||
);
|
||||
},
|
||||
row: (props) => {
|
||||
return <SortableRow {...props}></SortableRow>;
|
||||
},
|
||||
cell: (props) => (
|
||||
<td
|
||||
{...props}
|
||||
className={classNames(
|
||||
props.className,
|
||||
css`
|
||||
max-width: 300px;
|
||||
white-space: nowrap;
|
||||
.nb-read-pretty-input-number {
|
||||
text-align: right;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
const defaultRowKey = (record: any) => {
|
||||
return field.value?.indexOf?.(record);
|
||||
};
|
||||
}, [field, onRowDragEnd, dragSort]);
|
||||
|
||||
const defaultRowKey = (record: any) => {
|
||||
return field.value?.indexOf?.(record);
|
||||
};
|
||||
const getRowKey = (record: any) => {
|
||||
if (typeof rowKey === 'string') {
|
||||
return record[rowKey]?.toString();
|
||||
} else {
|
||||
return (rowKey ?? defaultRowKey)(record)?.toString();
|
||||
}
|
||||
};
|
||||
|
||||
const getRowKey = (record: any) => {
|
||||
if (typeof rowKey === 'string') {
|
||||
return record[rowKey]?.toString();
|
||||
} else {
|
||||
return (rowKey ?? defaultRowKey)(record)?.toString();
|
||||
}
|
||||
};
|
||||
|
||||
const restProps = {
|
||||
rowSelection: rowSelection
|
||||
? {
|
||||
const restProps = {
|
||||
rowSelection: rowSelection
|
||||
? {
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: selectedRowKeys,
|
||||
onChange(selectedRowKeys: any[], selectedRows: any[]) {
|
||||
@ -348,40 +349,40 @@ export const Table: any = observer((props: any) => {
|
||||
className={classNames(
|
||||
checked ? 'checked' : null,
|
||||
css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
float: left;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
padding-right: 8px;
|
||||
.nb-table-index {
|
||||
opacity: 0;
|
||||
}
|
||||
&:not(.checked) {
|
||||
.nb-table-index {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
position: relative;
|
||||
display: flex;
|
||||
float: left;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
padding-right: 8px;
|
||||
.nb-table-index {
|
||||
opacity: 0;
|
||||
}
|
||||
.nb-origin-node {
|
||||
display: block;
|
||||
&:not(.checked) {
|
||||
.nb-table-index {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
&:hover {
|
||||
.nb-table-index {
|
||||
opacity: 0;
|
||||
}
|
||||
.nb-origin-node {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
checked ? 'checked' : null,
|
||||
css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
`,
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
`,
|
||||
)}
|
||||
>
|
||||
{dragSort && <SortHandle id={getRowKey(record)} />}
|
||||
@ -392,13 +393,13 @@ export const Table: any = observer((props: any) => {
|
||||
'nb-origin-node',
|
||||
checked ? 'checked' : null,
|
||||
css`
|
||||
position: absolute;
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
&:not(.checked) {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
position: absolute;
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
&:not(.checked) {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
{originNode}
|
||||
@ -408,94 +409,96 @@ export const Table: any = observer((props: any) => {
|
||||
},
|
||||
...rowSelection,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const SortableWrapper = useCallback<React.FC>(
|
||||
({ children }) => {
|
||||
return dragSort
|
||||
? React.createElement(SortableContext, {
|
||||
const SortableWrapper = useCallback<React.FC>(
|
||||
({ children }) => {
|
||||
return dragSort
|
||||
? React.createElement(SortableContext, {
|
||||
items: field.value.map(getRowKey),
|
||||
children: children,
|
||||
})
|
||||
: React.createElement(React.Fragment, {
|
||||
: React.createElement(React.Fragment, {
|
||||
children,
|
||||
});
|
||||
},
|
||||
[field, dragSort],
|
||||
);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const fixedBlock = fieldSchema?.parent?.['x-decorator-props']?.fixedBlock;
|
||||
},
|
||||
[field, dragSort],
|
||||
);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const fixedBlock = fieldSchema?.parent?.['x-decorator-props']?.fixedBlock;
|
||||
|
||||
const { height: tableHeight, tableSizeRefCallback } = useTableSize();
|
||||
const { height: tableHeight, tableSizeRefCallback } = useTableSize();
|
||||
|
||||
const scroll = useMemo(() => {
|
||||
return fixedBlock
|
||||
? {
|
||||
const scroll = useMemo(() => {
|
||||
return fixedBlock
|
||||
? {
|
||||
x: 'max-content',
|
||||
y: tableHeight,
|
||||
}
|
||||
: {
|
||||
: {
|
||||
x: 'max-content',
|
||||
};
|
||||
}, [fixedBlock, tableHeight]);
|
||||
}, [fixedBlock, tableHeight]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.ant-table-wrapper {
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
height: 100%;
|
||||
.ant-spin-nested-loading {
|
||||
overflow: hidden;
|
||||
.ant-table-wrapper {
|
||||
height: 100%;
|
||||
.ant-spin-container {
|
||||
.ant-spin-nested-loading {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.ant-spin-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-table {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<SortableWrapper>
|
||||
<AntdTable
|
||||
ref={tableSizeRefCallback}
|
||||
rowKey={rowKey ?? defaultRowKey}
|
||||
dataSource={field?.value?.slice?.()}
|
||||
{...others}
|
||||
{...restProps}
|
||||
pagination={paginationProps}
|
||||
components={components}
|
||||
onChange={(pagination, filters, sorter, extra) => {
|
||||
onTableChange?.(pagination, filters, sorter, extra);
|
||||
}}
|
||||
onRow={onRow}
|
||||
rowClassName={(record) => (selectedRow.includes(record[rowKey]) ? highlightRow : '')}
|
||||
tableLayout={'auto'}
|
||||
scroll={scroll}
|
||||
columns={columns}
|
||||
expandable={{
|
||||
onExpand: (flag, record) => {
|
||||
const newKeys = flag ? [...expandedKeys, record.id] : expandedKeys.filter((i) => record.id !== i);
|
||||
setExpandesKeys(newKeys);
|
||||
onExpand?.(flag, record);
|
||||
},
|
||||
expandedRowKeys: expandedKeys,
|
||||
}}
|
||||
/>
|
||||
</SortableWrapper>
|
||||
{field.errors.length > 0 && (
|
||||
<div className="ant-formily-item-error-help ant-formily-item-help ant-formily-item-help-enter ant-formily-item-help-enter-active">
|
||||
{field.errors.map((error) => {
|
||||
return error.messages.map((message) => <div>{message}</div>);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
.ant-table {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<SortableWrapper>
|
||||
<AntdTable
|
||||
ref={tableSizeRefCallback}
|
||||
rowKey={rowKey ?? defaultRowKey}
|
||||
dataSource={field?.value?.slice?.()}
|
||||
{...others}
|
||||
{...restProps}
|
||||
pagination={paginationProps}
|
||||
components={components}
|
||||
onChange={(pagination, filters, sorter, extra) => {
|
||||
onTableChange?.(pagination, filters, sorter, extra);
|
||||
}}
|
||||
onRow={onRow}
|
||||
rowClassName={(record) => (selectedRow.includes(record[rowKey]) ? highlightRow : '')}
|
||||
tableLayout={'auto'}
|
||||
scroll={scroll}
|
||||
columns={columns}
|
||||
expandable={{
|
||||
onExpand: (flag, record) => {
|
||||
const newKeys = flag ? [...expandedKeys, record.id] : expandedKeys.filter((i) => record.id !== i);
|
||||
setExpandesKeys(newKeys);
|
||||
onExpand?.(flag, record);
|
||||
},
|
||||
expandedRowKeys: expandedKeys,
|
||||
}}
|
||||
/>
|
||||
</SortableWrapper>
|
||||
{field.errors.length > 0 && (
|
||||
<div className="ant-formily-item-error-help ant-formily-item-help ant-formily-item-help-enter ant-formily-item-help-enter-active">
|
||||
{field.errors.map((error) => {
|
||||
return error.messages.map((message) => <div>{message}</div>);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Table' },
|
||||
);
|
||||
|
@ -6,36 +6,42 @@ import { useCollection } from '../../../collection-manager';
|
||||
import { useCompile } from '../../hooks';
|
||||
import { ActionBar } from '../action';
|
||||
|
||||
export const TableField: any = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { getField } = useCollection();
|
||||
const field = useField<Field>();
|
||||
const collectionField = getField(fieldSchema.name);
|
||||
const compile = useCompile();
|
||||
const ctx = useFormBlockContext();
|
||||
useEffect(() => {
|
||||
if (!field.title) {
|
||||
field.title = compile(collectionField?.uiSchema?.title);
|
||||
}
|
||||
if (ctx?.field) {
|
||||
ctx.field.added = ctx.field.added || new Set();
|
||||
ctx.field.added.add(fieldSchema.name);
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
field.decoratorProps.asterisk = fieldSchema.required;
|
||||
}, [fieldSchema.required]);
|
||||
return <div>{props.children}</div>;
|
||||
});
|
||||
export const TableField: any = observer(
|
||||
(props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { getField } = useCollection();
|
||||
const field = useField<Field>();
|
||||
const collectionField = getField(fieldSchema.name);
|
||||
const compile = useCompile();
|
||||
const ctx = useFormBlockContext();
|
||||
useEffect(() => {
|
||||
if (!field.title) {
|
||||
field.title = compile(collectionField?.uiSchema?.title);
|
||||
}
|
||||
if (ctx?.field) {
|
||||
ctx.field.added = ctx.field.added || new Set();
|
||||
ctx.field.added.add(fieldSchema.name);
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
field.decoratorProps.asterisk = fieldSchema.required;
|
||||
}, [fieldSchema.required]);
|
||||
return <div>{props.children}</div>;
|
||||
},
|
||||
{ displayName: 'TableField' },
|
||||
);
|
||||
|
||||
TableField.ActionBar = observer((props) => {
|
||||
const form = useForm();
|
||||
if (form.readPretty) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div style={{ marginBottom: 8, marginTop: -32 }}>
|
||||
<ActionBar {...props} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
TableField.ActionBar = observer(
|
||||
(props) => {
|
||||
const form = useForm();
|
||||
if (form.readPretty) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div style={{ marginBottom: 8, marginTop: -32 }}>
|
||||
<ActionBar {...props} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TableField.ActionBar' },
|
||||
);
|
||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import { Table, TableColumnType } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const ArrayTable = observer((props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
const ArrayTable = observer(
|
||||
(props: any) => {
|
||||
const { rowKey } = props;
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === 'ArrayTable.Column') {
|
||||
buf.push(s);
|
||||
}
|
||||
return buf;
|
||||
}, []);
|
||||
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
const columns = columnSchemas.map((s) => {
|
||||
return {
|
||||
render: (value, record) => {
|
||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||
},
|
||||
} as TableColumnType<any>;
|
||||
});
|
||||
|
||||
return <Table {...props} rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
});
|
||||
return <Table {...props} rowKey={rowKey} columns={columns} dataSource={field.value} />;
|
||||
},
|
||||
{ displayName: 'ArrayTable' },
|
||||
);
|
||||
|
||||
const Value = connect((props) => {
|
||||
return <li>value: {props.value}</li>;
|
||||
|
@ -151,28 +151,29 @@ const useDefAction = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const TableArray: React.FC<any> = observer((props) => {
|
||||
const field = useField<ArrayField>();
|
||||
const columns = useTableColumns();
|
||||
const {
|
||||
dragSort = false,
|
||||
showIndex = true,
|
||||
useSelectedRowKeys = useDef,
|
||||
useDataSource = useDefDataSource,
|
||||
useAction = useDefAction,
|
||||
onChange,
|
||||
...others
|
||||
} = props;
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||
useDataSource({
|
||||
onSuccess(data) {
|
||||
field.value = data?.data || [];
|
||||
},
|
||||
});
|
||||
const { move } = useAction();
|
||||
const restProps = {
|
||||
rowSelection: props.rowSelection
|
||||
? {
|
||||
export const TableArray: React.FC<any> = observer(
|
||||
(props) => {
|
||||
const field = useField<ArrayField>();
|
||||
const columns = useTableColumns();
|
||||
const {
|
||||
dragSort = false,
|
||||
showIndex = true,
|
||||
useSelectedRowKeys = useDef,
|
||||
useDataSource = useDefDataSource,
|
||||
useAction = useDefAction,
|
||||
onChange,
|
||||
...others
|
||||
} = props;
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||
useDataSource({
|
||||
onSuccess(data) {
|
||||
field.value = data?.data || [];
|
||||
},
|
||||
});
|
||||
const { move } = useAction();
|
||||
const restProps = {
|
||||
rowSelection: props.rowSelection
|
||||
? {
|
||||
type: 'checkbox',
|
||||
selectedRowKeys,
|
||||
onChange(selectedRowKeys: any[]) {
|
||||
@ -189,39 +190,39 @@ export const TableArray: React.FC<any> = observer((props) => {
|
||||
className={classNames(
|
||||
checked ? 'checked' : null,
|
||||
css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
padding-right: 8px;
|
||||
.nb-table-index {
|
||||
opacity: 0;
|
||||
}
|
||||
&:not(.checked) {
|
||||
.nb-table-index {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
padding-right: 8px;
|
||||
.nb-table-index {
|
||||
opacity: 0;
|
||||
}
|
||||
.nb-origin-node {
|
||||
display: block;
|
||||
&:not(.checked) {
|
||||
.nb-table-index {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
&:hover {
|
||||
.nb-table-index {
|
||||
opacity: 0;
|
||||
}
|
||||
.nb-origin-node {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
checked ? 'checked' : null,
|
||||
css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
`,
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
`,
|
||||
)}
|
||||
>
|
||||
{dragSort && <SortHandle />}
|
||||
@ -232,13 +233,13 @@ export const TableArray: React.FC<any> = observer((props) => {
|
||||
'nb-origin-node',
|
||||
checked ? 'checked' : null,
|
||||
css`
|
||||
position: absolute;
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
&:not(.checked) {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
position: absolute;
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
&:not(.checked) {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
)}
|
||||
>
|
||||
{originNode}
|
||||
@ -248,43 +249,45 @@ export const TableArray: React.FC<any> = observer((props) => {
|
||||
},
|
||||
...props.rowSelection,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const defaultRowKey = (record: any) => {
|
||||
return field.value?.indexOf?.(record);
|
||||
};
|
||||
const defaultRowKey = (record: any) => {
|
||||
return field.value?.indexOf?.(record);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
.ant-table {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<ReactDragListView
|
||||
handleSelector={'.drag-handle'}
|
||||
onDragEnd={async (fromIndex, toIndex) => {
|
||||
const from = field.value[fromIndex];
|
||||
const to = field.value[toIndex];
|
||||
field.move(fromIndex, toIndex);
|
||||
await move(from, to);
|
||||
}}
|
||||
lineClassName={css`
|
||||
border-bottom: 2px solid rgba(241, 139, 98, 0.6) !important;
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
.ant-table {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Table
|
||||
rowKey={defaultRowKey}
|
||||
{...others}
|
||||
{...restProps}
|
||||
components={components}
|
||||
columns={columns}
|
||||
dataSource={field?.value?.slice?.()}
|
||||
/>
|
||||
</ReactDragListView>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
<ReactDragListView
|
||||
handleSelector={'.drag-handle'}
|
||||
onDragEnd={async (fromIndex, toIndex) => {
|
||||
const from = field.value[fromIndex];
|
||||
const to = field.value[toIndex];
|
||||
field.move(fromIndex, toIndex);
|
||||
await move(from, to);
|
||||
}}
|
||||
lineClassName={css`
|
||||
border-bottom: 2px solid rgba(241, 139, 98, 0.6) !important;
|
||||
`}
|
||||
>
|
||||
<Table
|
||||
rowKey={defaultRowKey}
|
||||
{...others}
|
||||
{...restProps}
|
||||
components={components}
|
||||
columns={columns}
|
||||
dataSource={field?.value?.slice?.()}
|
||||
/>
|
||||
</ReactDragListView>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TableArray' },
|
||||
);
|
||||
|
@ -42,12 +42,15 @@ export const designerCss = css`
|
||||
}
|
||||
`;
|
||||
|
||||
export const TableColumnActionBar = observer((props) => {
|
||||
const Designer = useDesigner();
|
||||
return (
|
||||
<SortableItem className={designerCss}>
|
||||
<Designer />
|
||||
{props.children}
|
||||
</SortableItem>
|
||||
);
|
||||
});
|
||||
export const TableColumnActionBar = observer(
|
||||
(props) => {
|
||||
const Designer = useDesigner();
|
||||
return (
|
||||
<SortableItem className={designerCss}>
|
||||
<Designer />
|
||||
{props.children}
|
||||
</SortableItem>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TableColumnActionBar' },
|
||||
);
|
||||
|
@ -9,20 +9,23 @@ type Props = TableProps<any> & { value?: any; onChange?: any; objectValue?: bool
|
||||
|
||||
const toArr = (value: any) => (isArr(value) ? value : isValid(value) ? [value] : []);
|
||||
|
||||
export const TableRowSelection = observer((props: Props) => {
|
||||
const { rowKey = 'id', objectValue } = props;
|
||||
const field = useField<Field>();
|
||||
const rowSelection: any = {
|
||||
type: 'checkbox',
|
||||
...props.rowSelection,
|
||||
selectedRowKeys: toArr(field.value).map((val) => (typeof val === 'object' ? val[rowKey as any] : val)),
|
||||
onChange(selectedRowKeys: any[], selectedRows?: any) {
|
||||
if (rowSelection.type === 'checkbox') {
|
||||
props.onChange(objectValue ? selectedRows : selectedRowKeys);
|
||||
} else {
|
||||
props.onChange([...(objectValue ? selectedRows : selectedRowKeys)].shift());
|
||||
}
|
||||
},
|
||||
};
|
||||
return <TableVoid {...props} rowSelection={rowSelection} />;
|
||||
});
|
||||
export const TableRowSelection = observer(
|
||||
(props: Props) => {
|
||||
const { rowKey = 'id', objectValue } = props;
|
||||
const field = useField<Field>();
|
||||
const rowSelection: any = {
|
||||
type: 'checkbox',
|
||||
...props.rowSelection,
|
||||
selectedRowKeys: toArr(field.value).map((val) => (typeof val === 'object' ? val[rowKey as any] : val)),
|
||||
onChange(selectedRowKeys: any[], selectedRows?: any) {
|
||||
if (rowSelection.type === 'checkbox') {
|
||||
props.onChange(objectValue ? selectedRows : selectedRowKeys);
|
||||
} else {
|
||||
props.onChange([...(objectValue ? selectedRows : selectedRowKeys)].shift());
|
||||
}
|
||||
},
|
||||
};
|
||||
return <TableVoid {...props} rowSelection={rowSelection} />;
|
||||
},
|
||||
{ displayName: 'TableRowSelection' },
|
||||
);
|
||||
|
@ -80,45 +80,48 @@ const useDefSelectedRowKeys = () => {
|
||||
return [result?.state?.selectedRowKeys, (selectedRowKeys) => result?.setState?.({ selectedRowKeys })];
|
||||
};
|
||||
|
||||
export const TableVoid: React.FC<TableVoidProps> = observer((props) => {
|
||||
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||
const field = useField<Field>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo(() => createForm(), []);
|
||||
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
||||
const result = useDataSource(
|
||||
{
|
||||
uid: fieldSchema['x-uid'],
|
||||
onSuccess(data) {
|
||||
form.setValues({
|
||||
[fieldSchema.name]: data?.data,
|
||||
});
|
||||
if (field?.componentProps?.pagination === false) {
|
||||
return;
|
||||
}
|
||||
field.componentProps.pagination = field.componentProps.pagination || {};
|
||||
if (data?.meta?.count) {
|
||||
field.componentProps.pagination.total = data?.meta?.count;
|
||||
}
|
||||
field.componentProps.pagination.current = data?.meta?.page || 1;
|
||||
field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10;
|
||||
export const TableVoid: React.FC<TableVoidProps> = observer(
|
||||
(props) => {
|
||||
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||
const field = useField<Field>();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo(() => createForm(), []);
|
||||
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
||||
const result = useDataSource(
|
||||
{
|
||||
uid: fieldSchema['x-uid'],
|
||||
onSuccess(data) {
|
||||
form.setValues({
|
||||
[fieldSchema.name]: data?.data,
|
||||
});
|
||||
if (field?.componentProps?.pagination === false) {
|
||||
return;
|
||||
}
|
||||
field.componentProps.pagination = field.componentProps.pagination || {};
|
||||
if (data?.meta?.count) {
|
||||
field.componentProps.pagination.total = data?.meta?.count;
|
||||
}
|
||||
field.componentProps.pagination.current = data?.meta?.page || 1;
|
||||
field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10;
|
||||
},
|
||||
},
|
||||
},
|
||||
props,
|
||||
);
|
||||
return (
|
||||
<AsyncDataProvider value={result}>
|
||||
<FormContext.Provider value={form}>
|
||||
<FieldContext.Provider value={f}>
|
||||
<TableArray
|
||||
{...props}
|
||||
rowKey={rowKey}
|
||||
useSelectedRowKeys={useSelectedRowKeys}
|
||||
loading={result?.loading}
|
||||
pagination={usePaginationProps(props, result)}
|
||||
/>
|
||||
</FieldContext.Provider>
|
||||
</FormContext.Provider>
|
||||
</AsyncDataProvider>
|
||||
);
|
||||
});
|
||||
props,
|
||||
);
|
||||
return (
|
||||
<AsyncDataProvider value={result}>
|
||||
<FormContext.Provider value={form}>
|
||||
<FieldContext.Provider value={f}>
|
||||
<TableArray
|
||||
{...props}
|
||||
rowKey={rowKey}
|
||||
useSelectedRowKeys={useSelectedRowKeys}
|
||||
loading={result?.loading}
|
||||
pagination={usePaginationProps(props, result)}
|
||||
/>
|
||||
</FieldContext.Provider>
|
||||
</FormContext.Provider>
|
||||
</AsyncDataProvider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TableVoid' },
|
||||
);
|
||||
|
@ -184,40 +184,43 @@ const schema: ISchema = {
|
||||
},
|
||||
};
|
||||
|
||||
const DataSourceProvider = observer((props) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const service = useRequest(
|
||||
() => {
|
||||
return Promise.resolve({
|
||||
data: [
|
||||
{ id: 1, name: 'Name1' },
|
||||
{ id: 2, name: 'Name2' },
|
||||
{ id: 3, name: 'Name3' },
|
||||
],
|
||||
});
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
setDataSource(data.data);
|
||||
const DataSourceProvider = observer(
|
||||
(props) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const service = useRequest(
|
||||
() => {
|
||||
return Promise.resolve({
|
||||
data: [
|
||||
{ id: 1, name: 'Name1' },
|
||||
{ id: 2, name: 'Name2' },
|
||||
{ id: 3, name: 'Name3' },
|
||||
],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
console.log('dataSource1', dataSource);
|
||||
return (
|
||||
<DataSourceContext.Provider
|
||||
value={{
|
||||
service,
|
||||
dataSource,
|
||||
setDataSource,
|
||||
selectedRowKeys,
|
||||
setSelectedRowKeys,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DataSourceContext.Provider>
|
||||
);
|
||||
});
|
||||
{
|
||||
onSuccess(data) {
|
||||
setDataSource(data.data);
|
||||
},
|
||||
},
|
||||
);
|
||||
console.log('dataSource1', dataSource);
|
||||
return (
|
||||
<DataSourceContext.Provider
|
||||
value={{
|
||||
service,
|
||||
dataSource,
|
||||
setDataSource,
|
||||
selectedRowKeys,
|
||||
setSelectedRowKeys,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DataSourceContext.Provider>
|
||||
);
|
||||
},
|
||||
{ displayName: 'DataSourceProvider' },
|
||||
);
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
|
@ -9,28 +9,31 @@ import { DndContext, SortableItem } from '../../common';
|
||||
import { useDesigner } from '../../hooks/useDesigner';
|
||||
import { TabsDesigner } from './Tabs.Designer';
|
||||
|
||||
export const Tabs: any = observer((props: TabsProps) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
return (
|
||||
<DndContext>
|
||||
<AntdTabs
|
||||
style={props.style}
|
||||
tabBarExtraContent={{
|
||||
right: render(),
|
||||
}}
|
||||
>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
return (
|
||||
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
|
||||
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
||||
</AntdTabs.TabPane>
|
||||
);
|
||||
})}
|
||||
</AntdTabs>
|
||||
</DndContext>
|
||||
);
|
||||
});
|
||||
export const Tabs: any = observer(
|
||||
(props: TabsProps) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
return (
|
||||
<DndContext>
|
||||
<AntdTabs
|
||||
style={props.style}
|
||||
tabBarExtraContent={{
|
||||
right: render(),
|
||||
}}
|
||||
>
|
||||
{fieldSchema.mapProperties((schema, key) => {
|
||||
return (
|
||||
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
|
||||
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
||||
</AntdTabs.TabPane>
|
||||
);
|
||||
})}
|
||||
</AntdTabs>
|
||||
</DndContext>
|
||||
);
|
||||
},
|
||||
{ displayName: 'Tabs' },
|
||||
);
|
||||
|
||||
const designerCss = css`
|
||||
position: relative;
|
||||
@ -79,15 +82,18 @@ const designerCss = css`
|
||||
}
|
||||
`;
|
||||
|
||||
Tabs.TabPane = observer((props: TabPaneProps & { icon?: any }) => {
|
||||
const Designer = useDesigner();
|
||||
const field = useField();
|
||||
return (
|
||||
<SortableItem className={classNames('nb-action-link', designerCss, props.className)}>
|
||||
{props.icon && <Icon style={{ marginRight: 2 }} type={props.icon} />} {props.tab || field.title}
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
);
|
||||
});
|
||||
Tabs.TabPane = observer(
|
||||
(props: TabPaneProps & { icon?: any }) => {
|
||||
const Designer = useDesigner();
|
||||
const field = useField();
|
||||
return (
|
||||
<SortableItem className={classNames('nb-action-link', designerCss, props.className)}>
|
||||
{props.icon && <Icon style={{ marginRight: 2 }} type={props.icon} />} {props.tab || field.title}
|
||||
<Designer />
|
||||
</SortableItem>
|
||||
);
|
||||
},
|
||||
{ displayName: 'TabPane' },
|
||||
);
|
||||
|
||||
Tabs.Designer = TabsDesigner;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user