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}</>
|
return <>{props.children}</>
|
||||||
});
|
});
|
||||||
|
HelloProvider.displayName = 'HelloProvider'
|
||||||
|
|
||||||
app.use(HelloProvider);
|
app.use(HelloProvider);
|
||||||
```
|
```
|
||||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
|||||||
import { Table, TableColumnType } from 'antd';
|
import { Table, TableColumnType } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const ArrayTable = observer((props: any) => {
|
const ArrayTable = observer(
|
||||||
const { rowKey } = props;
|
(props: any) => {
|
||||||
const field = useField<ArrayField>();
|
const { rowKey } = props;
|
||||||
const schema = useFieldSchema();
|
const field = useField<ArrayField>();
|
||||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
const schema = useFieldSchema();
|
||||||
if (s['x-component'] === 'ArrayTable.Column') {
|
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||||
buf.push(s);
|
if (s['x-component'] === 'ArrayTable.Column') {
|
||||||
}
|
buf.push(s);
|
||||||
return buf;
|
}
|
||||||
}, []);
|
return buf;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const columns = columnSchemas.map((s) => {
|
const columns = columnSchemas.map((s) => {
|
||||||
return {
|
return {
|
||||||
render: (value, record) => {
|
render: (value, record) => {
|
||||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||||
},
|
},
|
||||||
} as TableColumnType<any>;
|
} 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
|
@ -180,11 +180,11 @@ const Hello = observer((props) => {
|
|||||||
<div style={{ margin: 50 }}>{props.children}</div>
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
@ -240,7 +240,7 @@ const useDragEnd = () => {
|
|||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
function Draggable(props) {
|
function Draggable(props) {
|
||||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||||
@ -289,7 +289,7 @@ const Block = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Droppable>
|
</Droppable>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Block' });
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@ -316,7 +316,7 @@ export default function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
## Applications of `x-designer`
|
## Applications of `x-designer`
|
||||||
|
|
||||||
`x-designer` is usually used only in wrapper components such as BlockItem, CardItem, FormItem, etc.
|
`x-designer` is usually used only in wrapper components such as BlockItem, CardItem, FormItem, etc.
|
||||||
|
|
||||||
|
@ -51,13 +51,13 @@ const ReadPretty = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SingleText = connect(
|
const SingleText = connect(
|
||||||
Input,
|
Input,
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
suffix: '后缀',
|
suffix: '后缀',
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
mapReadPretty(ReadPretty),
|
mapReadPretty(ReadPretty),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ const SingleText = connect(Input);
|
|||||||
const UsedObserver = observer((props) => {
|
const UsedObserver = observer((props) => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
return <div>UsedObserver: {form.values.t1}</div>
|
return <div>UsedObserver: {form.values.t1}</div>
|
||||||
});
|
}, { displayName: 'UsedObserver' });
|
||||||
|
|
||||||
const NotUsedObserver = (props) => {
|
const NotUsedObserver = (props) => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
@ -192,7 +192,7 @@ export default () => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Comparison of rendering results by property type
|
Comparison of rendering results by property type
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -310,7 +310,7 @@ const ArrayList = observer((props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'ArrayList' });
|
||||||
|
|
||||||
const Value = connect((props) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>
|
return <li>value: {props.value}</li>
|
||||||
@ -367,7 +367,7 @@ const ArrayList = observer((props) => {
|
|||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'ArrayList' });
|
||||||
|
|
||||||
const Value = connect((props) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>
|
return <li>value: {props.value}</li>
|
||||||
@ -432,7 +432,7 @@ const ArrayTable = observer((props: 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
|
@ -116,11 +116,11 @@ const Hello = observer((props) => {
|
|||||||
<div style={{ margin: 50 }}>{props.children}</div>
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
|
@ -182,6 +182,7 @@ const HelloProvider = React.memo((props) => {
|
|||||||
}
|
}
|
||||||
return <>{props.children}</>
|
return <>{props.children}</>
|
||||||
});
|
});
|
||||||
|
HelloProvider.displayName = 'HelloProvider'
|
||||||
|
|
||||||
app.use(HelloProvider);
|
app.use(HelloProvider);
|
||||||
|
|
||||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
|||||||
import { Table, TableColumnType } from 'antd';
|
import { Table, TableColumnType } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const ArrayTable = observer((props: any) => {
|
const ArrayTable = observer(
|
||||||
const { rowKey } = props;
|
(props: any) => {
|
||||||
const field = useField<ArrayField>();
|
const { rowKey } = props;
|
||||||
const schema = useFieldSchema();
|
const field = useField<ArrayField>();
|
||||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
const schema = useFieldSchema();
|
||||||
if (s['x-component'] === 'ArrayTable.Column') {
|
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||||
buf.push(s);
|
if (s['x-component'] === 'ArrayTable.Column') {
|
||||||
}
|
buf.push(s);
|
||||||
return buf;
|
}
|
||||||
}, []);
|
return buf;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const columns = columnSchemas.map((s) => {
|
const columns = columnSchemas.map((s) => {
|
||||||
return {
|
return {
|
||||||
render: (value, record) => {
|
render: (value, record) => {
|
||||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||||
},
|
},
|
||||||
} as TableColumnType<any>;
|
} 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
|
@ -183,11 +183,11 @@ const Hello = observer((props) => {
|
|||||||
<div style={{ margin: 50 }}>{props.children}</div>
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
@ -243,7 +243,7 @@ const useDragEnd = () => {
|
|||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
function Draggable(props) {
|
function Draggable(props) {
|
||||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||||
@ -292,7 +292,7 @@ const Block = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Droppable>
|
</Droppable>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Block' });
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -51,13 +51,13 @@ const ReadPretty = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SingleText = connect(
|
const SingleText = connect(
|
||||||
Input,
|
Input,
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
suffix: '后缀',
|
suffix: '后缀',
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
mapReadPretty(ReadPretty),
|
mapReadPretty(ReadPretty),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ const SingleText = connect(Input);
|
|||||||
const UsedObserver = observer((props) => {
|
const UsedObserver = observer((props) => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
return <div>UsedObserver: {form.values.t1}</div>
|
return <div>UsedObserver: {form.values.t1}</div>
|
||||||
});
|
}, { displayName: 'UsedObserver' });
|
||||||
|
|
||||||
const NotUsedObserver = (props) => {
|
const NotUsedObserver = (props) => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
@ -193,7 +193,7 @@ export default () => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
各类型 properties 渲染结果对比
|
各类型 properties 渲染结果对比
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -312,7 +312,7 @@ const ArrayList = observer((props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'ArrayList' });
|
||||||
|
|
||||||
const Value = connect((props) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>
|
return <li>value: {props.value}</li>
|
||||||
@ -369,7 +369,7 @@ const ArrayList = observer((props) => {
|
|||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'ArrayList' });
|
||||||
|
|
||||||
const Value = connect((props) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>
|
return <li>value: {props.value}</li>
|
||||||
@ -434,7 +434,7 @@ const ArrayTable = observer((props: 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
@ -515,4 +515,4 @@ export default () => {
|
|||||||
</SchemaComponentProvider>
|
</SchemaComponentProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
@ -116,11 +116,11 @@ const Hello = observer((props) => {
|
|||||||
<div style={{ margin: 50 }}>{props.children}</div>
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
|
@ -42,6 +42,7 @@ const HelloProvider = React.memo((props) => {
|
|||||||
}
|
}
|
||||||
return <>{props.children}</>
|
return <>{props.children}</>
|
||||||
});
|
});
|
||||||
|
HelloProvider.displayName = 'HelloProvider'
|
||||||
|
|
||||||
app.use(HelloProvider);
|
app.use(HelloProvider);
|
||||||
```
|
```
|
||||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
|||||||
import { Table, TableColumnType } from 'antd';
|
import { Table, TableColumnType } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const ArrayTable = observer((props: any) => {
|
const ArrayTable = observer(
|
||||||
const { rowKey } = props;
|
(props: any) => {
|
||||||
const field = useField<ArrayField>();
|
const { rowKey } = props;
|
||||||
const schema = useFieldSchema();
|
const field = useField<ArrayField>();
|
||||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
const schema = useFieldSchema();
|
||||||
if (s['x-component'] === 'ArrayTable.Column') {
|
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||||
buf.push(s);
|
if (s['x-component'] === 'ArrayTable.Column') {
|
||||||
}
|
buf.push(s);
|
||||||
return buf;
|
}
|
||||||
}, []);
|
return buf;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const columns = columnSchemas.map((s) => {
|
const columns = columnSchemas.map((s) => {
|
||||||
return {
|
return {
|
||||||
render: (value, record) => {
|
render: (value, record) => {
|
||||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||||
},
|
},
|
||||||
} as TableColumnType<any>;
|
} 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
|
@ -183,11 +183,11 @@ const Hello = observer((props) => {
|
|||||||
<div style={{ margin: 50 }}>{props.children}</div>
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
@ -243,7 +243,7 @@ const useDragEnd = () => {
|
|||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
function Draggable(props) {
|
function Draggable(props) {
|
||||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||||
@ -292,7 +292,7 @@ const Block = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Droppable>
|
</Droppable>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Block' });
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -51,13 +51,13 @@ const ReadPretty = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SingleText = connect(
|
const SingleText = connect(
|
||||||
Input,
|
Input,
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
suffix: '后缀',
|
suffix: '后缀',
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
mapReadPretty(ReadPretty),
|
mapReadPretty(ReadPretty),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ const SingleText = connect(Input);
|
|||||||
const UsedObserver = observer((props) => {
|
const UsedObserver = observer((props) => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
return <div>UsedObserver: {form.values.t1}</div>
|
return <div>UsedObserver: {form.values.t1}</div>
|
||||||
});
|
}, { displayName: 'UsedObserver' });
|
||||||
|
|
||||||
const NotUsedObserver = (props) => {
|
const NotUsedObserver = (props) => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
@ -193,7 +193,7 @@ export default () => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
各类型 properties 渲染结果对比
|
各类型 properties 渲染结果对比
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -312,7 +312,7 @@ const ArrayList = observer((props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'ArrayList' });
|
||||||
|
|
||||||
const Value = connect((props) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>
|
return <li>value: {props.value}</li>
|
||||||
@ -369,7 +369,7 @@ const ArrayList = observer((props) => {
|
|||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'ArrayList' });
|
||||||
|
|
||||||
const Value = connect((props) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>
|
return <li>value: {props.value}</li>
|
||||||
@ -434,7 +434,7 @@ const ArrayTable = observer((props: 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
@ -515,4 +515,4 @@ export default () => {
|
|||||||
</SchemaComponentProvider>
|
</SchemaComponentProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
@ -116,11 +116,11 @@ const Hello = observer((props) => {
|
|||||||
<div style={{ margin: 50 }}>{props.children}</div>
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div>{props.children}</div>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
|
@ -176,6 +176,7 @@ const HelloProvider = React.memo((props) => {
|
|||||||
}
|
}
|
||||||
return <>{props.children}</>
|
return <>{props.children}</>
|
||||||
});
|
});
|
||||||
|
HelloProvider.displayName = 'HelloProvider'
|
||||||
|
|
||||||
app.use(HelloProvider);
|
app.use(HelloProvider);
|
||||||
```
|
```
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default React.memo((props) => {
|
const CommonMemo = React.memo((props) => {
|
||||||
return <>{props.children}</>;
|
return <>{props.children}</>;
|
||||||
});
|
});
|
||||||
|
CommonMemo.displayName = 'CommonMemo';
|
||||||
|
|
||||||
|
export default CommonMemo;
|
||||||
|
@ -168,11 +168,11 @@ const App: React.FC = () => {
|
|||||||
<Button type="primary" onClick={showDrawer}>
|
<Button type="primary" onClick={showDrawer}>
|
||||||
Open
|
Open
|
||||||
</Button>
|
</Button>
|
||||||
<Drawer
|
<Drawer
|
||||||
title="Basic Drawer"
|
title="Basic Drawer"
|
||||||
placement="right"
|
placement="right"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
footer={
|
footer={
|
||||||
<Button onClick={onClose}>关闭</Button>
|
<Button onClick={onClose}>关闭</Button>
|
||||||
}
|
}
|
||||||
@ -262,8 +262,8 @@ export default function App() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<SchemaComponentProvider
|
<SchemaComponentProvider
|
||||||
form={form}
|
form={form}
|
||||||
components={{ Drawer, Button }}
|
components={{ Drawer, Button }}
|
||||||
>
|
>
|
||||||
<SchemaComponent schema={schema} scope={{ showDrawer, onClose }} />
|
<SchemaComponent schema={schema} scope={{ showDrawer, onClose }} />
|
||||||
@ -374,7 +374,7 @@ const Action: any = observer((props: any) => {
|
|||||||
<RecursionField basePath={field.address} schema={schema} onlyRenderProperties />
|
<RecursionField basePath={field.address} schema={schema} onlyRenderProperties />
|
||||||
</VisibleContext.Provider>
|
</VisibleContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Action' });
|
||||||
|
|
||||||
Action.Drawer = observer((props: any) => {
|
Action.Drawer = observer((props: any) => {
|
||||||
const [visible, setVisible] = useContext(VisibleContext);
|
const [visible, setVisible] = useContext(VisibleContext);
|
||||||
@ -411,13 +411,13 @@ Action.Drawer = observer((props: any) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Action.Drawer' });
|
||||||
|
|
||||||
Action.Drawer.Footer = observer((props: any) => {
|
Action.Drawer.Footer = observer((props: any) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||||
});
|
}, { displayName: 'Action.Drawer.Footer' });
|
||||||
|
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@ -500,7 +500,7 @@ const Hello = observer(({ name }) => {
|
|||||||
>更新</Button>
|
>更新</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
});
|
}, { displayName: 'Hello' });
|
||||||
|
|
||||||
const RouteSchemaComponent = (props) => {
|
const RouteSchemaComponent = (props) => {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -678,7 +678,7 @@ const useDragEnd = () => {
|
|||||||
|
|
||||||
const Page = observer((props) => {
|
const Page = observer((props) => {
|
||||||
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
return <DndContext onDragEnd={useDragEnd()}>{props.children}</DndContext>;
|
||||||
});
|
}, { displayName: 'Page' });
|
||||||
|
|
||||||
function Draggable(props) {
|
function Draggable(props) {
|
||||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||||
@ -727,7 +727,7 @@ const Block = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Droppable>
|
</Droppable>
|
||||||
);
|
);
|
||||||
});
|
}, { displayName: 'Block' });
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@ -856,4 +856,4 @@ await respond(ctx);
|
|||||||
- Provider 的顺序怎么处理
|
- Provider 的顺序怎么处理
|
||||||
- 如何动态的加载前端模块
|
- 如何动态的加载前端模块
|
||||||
|
|
||||||
未完待续...
|
未完待续...
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
"@dnd-kit/core": "^5.0.1",
|
"@dnd-kit/core": "^5.0.1",
|
||||||
"@dnd-kit/sortable": "^6.0.0",
|
"@dnd-kit/sortable": "^6.0.0",
|
||||||
"@emotion/css": "^11.7.1",
|
"@emotion/css": "^11.7.1",
|
||||||
"@formily/antd": "2.0.20",
|
"@formily/antd": "2.2.24",
|
||||||
"@formily/core": "2.0.20",
|
"@formily/core": "2.2.24",
|
||||||
"@formily/react": "2.0.20",
|
"@formily/react": "2.2.24",
|
||||||
"@nocobase/evaluators": "0.9.4-alpha.2",
|
"@nocobase/evaluators": "0.9.4-alpha.2",
|
||||||
"@nocobase/sdk": "0.9.4-alpha.2",
|
"@nocobase/sdk": "0.9.4-alpha.2",
|
||||||
"@nocobase/utils": "0.9.4-alpha.2",
|
"@nocobase/utils": "0.9.4-alpha.2",
|
||||||
|
@ -61,6 +61,8 @@ const App = React.memo((props: any) => {
|
|||||||
return <C />;
|
return <C />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App.displayName = 'App';
|
||||||
|
|
||||||
export class Application {
|
export class Application {
|
||||||
providers = [];
|
providers = [];
|
||||||
mainComponent = null;
|
mainComponent = null;
|
||||||
|
@ -3,18 +3,21 @@ import { useDesignable } from '@nocobase/client';
|
|||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { observer } from '@formily/react';
|
import { observer } from '@formily/react';
|
||||||
|
|
||||||
export const Hello: React.FC<any> = observer(({ name }) => {
|
export const Hello: React.FC<any> = observer(
|
||||||
const { patch, remove } = useDesignable();
|
({ name }) => {
|
||||||
return (
|
const { patch, remove } = useDesignable();
|
||||||
<div>
|
return (
|
||||||
<h1>Hello {name}!</h1>
|
<div>
|
||||||
<Button
|
<h1>Hello {name}!</h1>
|
||||||
onClick={() => {
|
<Button
|
||||||
patch('x-component-props.name', Math.random());
|
onClick={() => {
|
||||||
}}
|
patch('x-component-props.name', Math.random());
|
||||||
>
|
}}
|
||||||
更新
|
>
|
||||||
</Button>
|
更新
|
||||||
</div>
|
</Button>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Hello' },
|
||||||
|
);
|
||||||
|
@ -31,36 +31,39 @@ const useDef = (options, props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CollectionFieldsTable: React.FC<TableVoidProps> = observer((props) => {
|
export const CollectionFieldsTable: React.FC<TableVoidProps> = observer(
|
||||||
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
(props) => {
|
||||||
const field = useField<Field>();
|
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||||
const fieldSchema = useFieldSchema();
|
const field = useField<Field>();
|
||||||
const form = useMemo(() => createForm(), []);
|
const fieldSchema = useFieldSchema();
|
||||||
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
const form = useMemo(() => createForm(), []);
|
||||||
const result = useDataSource(
|
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
||||||
{
|
const result = useDataSource(
|
||||||
uid: fieldSchema['x-uid'],
|
{
|
||||||
onSuccess(data) {
|
uid: fieldSchema['x-uid'],
|
||||||
form.setValues({
|
onSuccess(data) {
|
||||||
[fieldSchema.name]: data?.data,
|
form.setValues({
|
||||||
});
|
[fieldSchema.name]: data?.data,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
props,
|
||||||
props,
|
);
|
||||||
);
|
return (
|
||||||
return (
|
<AsyncDataProvider value={result}>
|
||||||
<AsyncDataProvider value={result}>
|
<FormContext.Provider value={form}>
|
||||||
<FormContext.Provider value={form}>
|
<FieldContext.Provider value={f}>
|
||||||
<FieldContext.Provider value={f}>
|
<CollectionFieldsTableArray
|
||||||
<CollectionFieldsTableArray
|
{...props}
|
||||||
{...props}
|
rowKey={rowKey}
|
||||||
rowKey={rowKey}
|
loading={result?.['loading']}
|
||||||
loading={result?.['loading']}
|
useSelectedRowKeys={useSelectedRowKeys}
|
||||||
useSelectedRowKeys={useSelectedRowKeys}
|
pagination={false}
|
||||||
pagination={false}
|
/>
|
||||||
/>
|
</FieldContext.Provider>
|
||||||
</FieldContext.Provider>
|
</FormContext.Provider>
|
||||||
</FormContext.Provider>
|
</AsyncDataProvider>
|
||||||
</AsyncDataProvider>
|
);
|
||||||
);
|
},
|
||||||
});
|
{ displayName: 'CollectionFieldsTable' },
|
||||||
|
);
|
||||||
|
@ -79,149 +79,150 @@ interface CategorizeDataItem {
|
|||||||
data: Array<any>;
|
data: Array<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CollectionFieldsTableArray: React.FC<any> = observer((props) => {
|
export const CollectionFieldsTableArray: React.FC<any> = observer(
|
||||||
const sortKeyArr: Array<CategorizeKey> = ['primaryAndForeignKey', 'relation', 'basic', 'systemInfo'];
|
(props) => {
|
||||||
const field = useField<ArrayField>();
|
const sortKeyArr: Array<CategorizeKey> = ['primaryAndForeignKey', 'relation', 'basic', 'systemInfo'];
|
||||||
const { name } = useRecord();
|
const field = useField<ArrayField>();
|
||||||
const { t } = useTranslation();
|
const { name } = useRecord();
|
||||||
const compile = useCompile();
|
const { t } = useTranslation();
|
||||||
const { getInterface, getInheritCollections, getCollection, getCurrentCollectionFields, getInheritedFields } =
|
const compile = useCompile();
|
||||||
useCollectionManager();
|
const { getInterface, getInheritCollections, getCollection, getCurrentCollectionFields, getInheritedFields } =
|
||||||
const {
|
useCollectionManager();
|
||||||
showIndex = true,
|
const {
|
||||||
useSelectedRowKeys = useDef,
|
showIndex = true,
|
||||||
useDataSource = useDefDataSource,
|
useSelectedRowKeys = useDef,
|
||||||
onChange,
|
useDataSource = useDefDataSource,
|
||||||
...others
|
onChange,
|
||||||
} = props;
|
...others
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
} = props;
|
||||||
const [categorizeData, setCategorizeData] = useState<Array<CategorizeDataItem>>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||||
const [expandedKeys, setExpendedKeys] = useState(selectedRowKeys);
|
const [categorizeData, setCategorizeData] = useState<Array<CategorizeDataItem>>([]);
|
||||||
const inherits = getInheritCollections(name);
|
const [expandedKeys, setExpendedKeys] = useState(selectedRowKeys);
|
||||||
const currentFields = getCurrentCollectionFields(name);
|
const inherits = getInheritCollections(name);
|
||||||
useDataSource({
|
const currentFields = getCurrentCollectionFields(name);
|
||||||
onSuccess(data) {
|
useDataSource({
|
||||||
field.value = data?.data || [];
|
onSuccess(data) {
|
||||||
const tmpData: Array<CategorizeDataItem> = [];
|
field.value = data?.data || [];
|
||||||
const categorizeMap = new Map<CategorizeKey, any>();
|
const tmpData: Array<CategorizeDataItem> = [];
|
||||||
const addCategorizeVal = (categorizeKey: CategorizeKey, val) => {
|
const categorizeMap = new Map<CategorizeKey, any>();
|
||||||
let fieldArr = categorizeMap.get(categorizeKey);
|
const addCategorizeVal = (categorizeKey: CategorizeKey, val) => {
|
||||||
if (!fieldArr) {
|
let fieldArr = categorizeMap.get(categorizeKey);
|
||||||
fieldArr = [];
|
if (!fieldArr) {
|
||||||
}
|
fieldArr = [];
|
||||||
fieldArr.push(val);
|
}
|
||||||
categorizeMap.set(categorizeKey, fieldArr);
|
fieldArr.push(val);
|
||||||
};
|
categorizeMap.set(categorizeKey, fieldArr);
|
||||||
field.value.forEach((item) => {
|
};
|
||||||
const itemInterface = getInterface(item?.interface);
|
field.value.forEach((item) => {
|
||||||
if (item?.primaryKey || item.isForeignKey) {
|
const itemInterface = getInterface(item?.interface);
|
||||||
addCategorizeVal('primaryAndForeignKey', item);
|
if (item?.primaryKey || item.isForeignKey) {
|
||||||
return;
|
addCategorizeVal('primaryAndForeignKey', item);
|
||||||
}
|
return;
|
||||||
const group = itemInterface?.group as CategorizeKey;
|
}
|
||||||
switch (group) {
|
const group = itemInterface?.group as CategorizeKey;
|
||||||
case 'systemInfo':
|
switch (group) {
|
||||||
case 'relation':
|
case 'systemInfo':
|
||||||
addCategorizeVal(group, item);
|
case 'relation':
|
||||||
break;
|
addCategorizeVal(group, item);
|
||||||
default:
|
break;
|
||||||
addCategorizeVal('basic', item);
|
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, {}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
if (inherits) {
|
||||||
sortKeyArr.forEach((key) => {
|
inherits.forEach((v) => {
|
||||||
if (categorizeMap.get(key)?.length > 0) {
|
sortKeyArr.push(v);
|
||||||
const parentCollection = getCollection(key);
|
const parentCollection = getCollection(v);
|
||||||
tmpData.push({
|
parentCollection.fields.map((k) => {
|
||||||
key,
|
if (k.interface) {
|
||||||
name:
|
addCategorizeVal(v, new Proxy(k, {}));
|
||||||
t(CategorizeKeyNameMap.get(key)) ||
|
field.value.push(new Proxy(k, {}));
|
||||||
t(`Parent collection fields`) + `(${compile(parentCollection.title)})`,
|
}
|
||||||
data: categorizeMap.get(key),
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
sortKeyArr.forEach((key) => {
|
||||||
setExpendedKeys(sortKeyArr);
|
if (categorizeMap.get(key)?.length > 0) {
|
||||||
setCategorizeData(tmpData);
|
const parentCollection = getCollection(key);
|
||||||
},
|
tmpData.push({
|
||||||
});
|
key,
|
||||||
const useTableColumns = () => {
|
name:
|
||||||
const schema = useFieldSchema();
|
t(CategorizeKeyNameMap.get(key)) ||
|
||||||
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
t(`Parent collection fields`) + `(${compile(parentCollection.title)})`,
|
||||||
const columns = schema
|
data: categorizeMap.get(key),
|
||||||
.reduceProperties((buf, s) => {
|
});
|
||||||
if (isColumnComponent(s)) {
|
}
|
||||||
return buf.concat([s]);
|
});
|
||||||
}
|
setExpendedKeys(sortKeyArr);
|
||||||
}, [])
|
setCategorizeData(tmpData);
|
||||||
.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',
|
|
||||||
});
|
});
|
||||||
};
|
const useTableColumns = () => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
const ExpandedRowRender = (record: CategorizeDataItem, index, indent, expanded) => {
|
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
||||||
const columns = useTableColumns();
|
const columns = schema
|
||||||
if (!props.loading) {
|
.reduceProperties((buf, s) => {
|
||||||
if (inherits.includes(record.key)) {
|
if (isColumnComponent(s)) {
|
||||||
columns.pop();
|
return buf.concat([s]);
|
||||||
columns.push({
|
}
|
||||||
title: <RecursionField name={'column4'} schema={overridingSchema as Schema} onlyRenderSelf />,
|
}, [])
|
||||||
dataIndex: 'column4',
|
.map((s: Schema) => {
|
||||||
key: 'column4',
|
return {
|
||||||
render: (v, record) => {
|
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
||||||
const index = findIndex(field.value, record);
|
dataIndex: s.name,
|
||||||
return (
|
key: s.name,
|
||||||
<RecordIndexProvider index={index}>
|
render: (v, record) => {
|
||||||
<RecordProvider record={record}>
|
const index = findIndex(field.value, record);
|
||||||
<SchemaComponent
|
return (
|
||||||
scope={{ currentCollection: name }}
|
<RecordIndexProvider index={index}>
|
||||||
schema={overridingSchema as Schema}
|
<RecordProvider record={record}>
|
||||||
name={index}
|
<RecursionField schema={s} name={index} onlyRenderProperties />
|
||||||
onlyRenderProperties
|
</RecordProvider>
|
||||||
/>
|
</RecordIndexProvider>
|
||||||
</RecordProvider>
|
);
|
||||||
</RecordIndexProvider>
|
},
|
||||||
);
|
} as TableColumnProps<any>;
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
if (!exists) {
|
||||||
|
return columns;
|
||||||
}
|
}
|
||||||
const restProps = {
|
return columns.concat({
|
||||||
rowSelection:
|
title: render(),
|
||||||
props.rowSelection && !inherits.includes(record.key)
|
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',
|
type: 'checkbox',
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
onChange(selectedRowKeys: any[]) {
|
onChange(selectedRowKeys: any[]) {
|
||||||
@ -229,52 +230,54 @@ export const CollectionFieldsTableArray: React.FC<any> = observer((props) => {
|
|||||||
},
|
},
|
||||||
...props.rowSelection,
|
...props.rowSelection,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
{...others}
|
{...others}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
components={components}
|
components={components}
|
||||||
showHeader={true}
|
showHeader={true}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={record.data}
|
dataSource={record.data}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css`
|
className={css`
|
||||||
.ant-table {
|
.ant-table {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
setExpendedKeys(keys);
|
`}
|
||||||
}}
|
>
|
||||||
/>
|
<Table
|
||||||
</div>
|
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 }) => {
|
const TabTitle = observer(
|
||||||
return (
|
({ item }: { item: any }) => {
|
||||||
<Droppable id={item.id.toString()} data={item}>
|
return (
|
||||||
<div>
|
<Droppable id={item.id.toString()} data={item}>
|
||||||
<Draggable id={item.id.toString()} data={item}>
|
<div>
|
||||||
<TabBar item={item} />
|
<Draggable id={item.id.toString()} data={item}>
|
||||||
</Draggable>
|
<TabBar item={item} />
|
||||||
</div>
|
</Draggable>
|
||||||
</Droppable>
|
</div>
|
||||||
);
|
</Droppable>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'TabTitle' },
|
||||||
|
);
|
||||||
|
|
||||||
const TabBar = ({ item }) => {
|
const TabBar = ({ item }) => {
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
@ -71,45 +74,48 @@ const TabBar = ({ item }) => {
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const DndProvider = observer((props) => {
|
const DndProvider = observer(
|
||||||
const [activeTab, setActiveId] = useState(null);
|
(props) => {
|
||||||
const { refresh } = useContext(CollectionCategroriesContext);
|
const [activeTab, setActiveId] = useState(null);
|
||||||
const { refresh: refreshCM } = useResourceActionContext();
|
const { refresh } = useContext(CollectionCategroriesContext);
|
||||||
const api = useAPIClient();
|
const { refresh: refreshCM } = useResourceActionContext();
|
||||||
const onDragEnd = async (props: DragEndEvent) => {
|
const api = useAPIClient();
|
||||||
const { active, over } = props;
|
const onDragEnd = async (props: DragEndEvent) => {
|
||||||
setTimeout(() => {
|
const { active, over } = props;
|
||||||
setActiveId(null);
|
setTimeout(() => {
|
||||||
});
|
setActiveId(null);
|
||||||
if (over && over.id !== active.id) {
|
|
||||||
await api.resource('collectionCategories').move({
|
|
||||||
sourceId: active.id,
|
|
||||||
targetId: over.id,
|
|
||||||
});
|
});
|
||||||
await refresh();
|
if (over && over.id !== active.id) {
|
||||||
await refreshCM();
|
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) {
|
const mouseSensor = useSensor(MouseSensor, {
|
||||||
setActiveId(event.active?.data.current);
|
activationConstraint: {
|
||||||
}
|
distance: 10,
|
||||||
|
},
|
||||||
const mouseSensor = useSensor(MouseSensor, {
|
});
|
||||||
activationConstraint: {
|
const sensors = useSensors(mouseSensor);
|
||||||
distance: 10,
|
return (
|
||||||
},
|
<DndContext sensors={sensors} onDragEnd={onDragEnd} onDragStart={onDragStart}>
|
||||||
});
|
{props.children}
|
||||||
const sensors = useSensors(mouseSensor);
|
<DragOverlay>
|
||||||
return (
|
{activeTab ? <span style={{ whiteSpace: 'nowrap' }}>{<TabBar item={activeTab} />}</span> : null}
|
||||||
<DndContext sensors={sensors} onDragEnd={onDragEnd} onDragStart={onDragStart}>
|
</DragOverlay>
|
||||||
{props.children}
|
</DndContext>
|
||||||
<DragOverlay>
|
);
|
||||||
{activeTab ? <span style={{ whiteSpace: 'nowrap' }}>{<TabBar item={activeTab} />}</span> : null}
|
},
|
||||||
</DragOverlay>
|
{ displayName: 'DndProvider' },
|
||||||
</DndContext>
|
);
|
||||||
);
|
|
||||||
});
|
|
||||||
export const ConfigurationTabs = () => {
|
export const ConfigurationTabs = () => {
|
||||||
const { data, refresh } = useContext(CollectionCategroriesContext);
|
const { data, refresh } = useContext(CollectionCategroriesContext);
|
||||||
const { refresh: refreshCM, run, defaultRequest, setState } = useResourceActionContext();
|
const { refresh: refreshCM, run, defaultRequest, setState } = useResourceActionContext();
|
||||||
|
@ -3,14 +3,17 @@ import { Tag } from 'antd';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCompile } from '../../../schema-component';
|
import { useCompile } from '../../../schema-component';
|
||||||
|
|
||||||
export const CollectionCategory = observer((props: any) => {
|
export const CollectionCategory = observer(
|
||||||
const { value } = props;
|
(props: any) => {
|
||||||
const compile = useCompile();
|
const { value } = props;
|
||||||
return (
|
const compile = useCompile();
|
||||||
<>
|
return (
|
||||||
{value.map((item) => {
|
<>
|
||||||
return <Tag color={item.color}>{compile(item?.name)}</Tag>;
|
{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 { useCompile } from '../../../schema-component';
|
||||||
import { useCollectionManager } from '../../hooks';
|
import { useCollectionManager } from '../../hooks';
|
||||||
|
|
||||||
export const CollectionFieldInterface = observer((props: any) => {
|
export const CollectionFieldInterface = observer(
|
||||||
const { value } = props;
|
(props: any) => {
|
||||||
const { getInterface } = useCollectionManager();
|
const { value } = props;
|
||||||
const compile = useCompile();
|
const { getInterface } = useCollectionManager();
|
||||||
const schema = getInterface(value);
|
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 { useCompile } from '../../../schema-component';
|
||||||
import { useCollectionManager } from '../../hooks';
|
import { useCollectionManager } from '../../hooks';
|
||||||
|
|
||||||
export const CollectionTemplate = observer((props: any) => {
|
export const CollectionTemplate = observer(
|
||||||
const { value } = props;
|
(props: any) => {
|
||||||
const { getTemplate } = useCollectionManager();
|
const { value } = props;
|
||||||
const compile = useCompile();
|
const { getTemplate } = useCollectionManager();
|
||||||
const schema = getTemplate(value);
|
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 { useCompile } from '../../../schema-component';
|
||||||
import { useCollectionManager } from '../../hooks';
|
import { useCollectionManager } from '../../hooks';
|
||||||
|
|
||||||
export const FieldSummary = observer((props: any) => {
|
export const FieldSummary = observer(
|
||||||
const { schemaKey } = props;
|
(props: any) => {
|
||||||
const { getInterface } = useCollectionManager();
|
const { schemaKey } = props;
|
||||||
const compile = useCompile();
|
const { getInterface } = useCollectionManager();
|
||||||
const { t } = useTranslation();
|
const compile = useCompile();
|
||||||
const schema = getInterface(schemaKey);
|
const { t } = useTranslation();
|
||||||
|
const schema = getInterface(schemaKey);
|
||||||
|
|
||||||
if (!schema) return null;
|
if (!schema) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css`
|
className={css`
|
||||||
background: #f6f6f6;
|
background: #f6f6f6;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className={css``}>
|
<div className={css``}>
|
||||||
{t('Field interface')}: <Tag>{compile(schema.title)}</Tag>
|
{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)}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
{schema.description ? (
|
||||||
</div>
|
<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 { useCompile } from '../../../schema-component';
|
||||||
import { useCollectionManager } from '../../hooks';
|
import { useCollectionManager } from '../../hooks';
|
||||||
|
|
||||||
export const TemplateSummay = observer((props: any) => {
|
export const TemplateSummay = observer(
|
||||||
const { schemaKey } = props;
|
(props: any) => {
|
||||||
const { getTemplate } = useCollectionManager();
|
const { schemaKey } = props;
|
||||||
const compile = useCompile();
|
const { getTemplate } = useCollectionManager();
|
||||||
const { t } = useTranslation();
|
const compile = useCompile();
|
||||||
const schema = getTemplate(schemaKey);
|
const { t } = useTranslation();
|
||||||
|
const schema = getTemplate(schemaKey);
|
||||||
|
|
||||||
if (!schema) return null;
|
if (!schema) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css`
|
className={css`
|
||||||
background: #f6f6f6;
|
background: #f6f6f6;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className={css``}>
|
<div className={css``}>
|
||||||
{t('Collection template')}: <Tag>{compile(schema.title)}</Tag>
|
{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)}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
{schema.description ? (
|
||||||
</div>
|
<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 { useCompile } from '../../../schema-component';
|
||||||
import { useCollectionManager } from '../../hooks';
|
import { useCollectionManager } from '../../hooks';
|
||||||
|
|
||||||
export const SourceForeignKey = observer(() => {
|
export const SourceForeignKey = observer(
|
||||||
const record = useRecord();
|
() => {
|
||||||
const { getCollection } = useCollectionManager();
|
const record = useRecord();
|
||||||
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
const { getCollection } = useCollectionManager();
|
||||||
const field = useField<Field>();
|
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
||||||
const form = useForm();
|
const field = useField<Field>();
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const form = useForm();
|
||||||
return (
|
const { getCollectionFields } = useCollectionManager();
|
||||||
<div>
|
return (
|
||||||
<Select
|
<div>
|
||||||
allowClear
|
<Select
|
||||||
placeholder={'留空时,自动生成 FK 字段'}
|
allowClear
|
||||||
disabled={field.disabled}
|
placeholder={'留空时,自动生成 FK 字段'}
|
||||||
value={field.value}
|
disabled={field.disabled}
|
||||||
options={getCollectionFields(collection.name)
|
value={field.value}
|
||||||
.filter((field) => field.type)
|
options={getCollectionFields(collection.name)
|
||||||
.map((field) => {
|
.filter((field) => field.type)
|
||||||
return {
|
.map((field) => {
|
||||||
label: field?.uiSchema?.title || field.name,
|
return {
|
||||||
value: field.name,
|
label: field?.uiSchema?.title || field.name,
|
||||||
};
|
value: field.name,
|
||||||
})}
|
};
|
||||||
/>
|
})}
|
||||||
</div>
|
/>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'SourceForeignKey' },
|
||||||
|
);
|
||||||
|
|
||||||
export const ThroughForeignKey = observer(() => {
|
export const ThroughForeignKey = observer(
|
||||||
const field = useField<Field>();
|
() => {
|
||||||
const form = useForm();
|
const field = useField<Field>();
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const form = useForm();
|
||||||
return (
|
const { getCollectionFields } = useCollectionManager();
|
||||||
<div>
|
return (
|
||||||
<Select
|
<div>
|
||||||
allowClear
|
<Select
|
||||||
placeholder={'留空时,自动生成 FK 字段'}
|
allowClear
|
||||||
disabled={field.disabled}
|
placeholder={'留空时,自动生成 FK 字段'}
|
||||||
value={field.value}
|
disabled={field.disabled}
|
||||||
options={getCollectionFields(form.values.through)
|
value={field.value}
|
||||||
.filter((field) => field.type)
|
options={getCollectionFields(form.values.through)
|
||||||
.map((field) => {
|
.filter((field) => field.type)
|
||||||
return {
|
.map((field) => {
|
||||||
label: field?.uiSchema?.title || field.name,
|
return {
|
||||||
value: field.name,
|
label: field?.uiSchema?.title || field.name,
|
||||||
};
|
value: field.name,
|
||||||
})}
|
};
|
||||||
/>
|
})}
|
||||||
</div>
|
/>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'ThroughForeignKey' },
|
||||||
|
);
|
||||||
|
|
||||||
export const TargetForeignKey = observer(() => {
|
export const TargetForeignKey = observer(
|
||||||
const field = useField<Field>();
|
() => {
|
||||||
const form = useForm();
|
const field = useField<Field>();
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const form = useForm();
|
||||||
return (
|
const { getCollectionFields } = useCollectionManager();
|
||||||
<div>
|
return (
|
||||||
<Select
|
<div>
|
||||||
allowClear
|
<Select
|
||||||
placeholder={'留空时,自动生成 FK 字段'}
|
allowClear
|
||||||
disabled={field.disabled}
|
placeholder={'留空时,自动生成 FK 字段'}
|
||||||
value={field.value}
|
disabled={field.disabled}
|
||||||
options={getCollectionFields(form.values.target)
|
value={field.value}
|
||||||
.filter((field) => field.type)
|
options={getCollectionFields(form.values.target)
|
||||||
.map((field) => {
|
.filter((field) => field.type)
|
||||||
return {
|
.map((field) => {
|
||||||
label: field?.uiSchema?.title || field.name,
|
return {
|
||||||
value: field.name,
|
label: field?.uiSchema?.title || field.name,
|
||||||
};
|
value: field.name,
|
||||||
})}
|
};
|
||||||
/>
|
})}
|
||||||
</div>
|
/>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'TargetForeignKey' },
|
||||||
|
);
|
||||||
|
|
||||||
export const SourceCollection = observer(() => {
|
export const SourceCollection = observer(
|
||||||
const record = useRecord();
|
() => {
|
||||||
const { getCollection } = useCollectionManager();
|
const record = useRecord();
|
||||||
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
const { getCollection } = useCollectionManager();
|
||||||
const compile = useCompile();
|
const collection = record?.collectionName ? getCollection(record.collectionName) : record;
|
||||||
return (
|
const compile = useCompile();
|
||||||
<div>
|
return (
|
||||||
<Select
|
<div>
|
||||||
disabled
|
<Select
|
||||||
value={collection.name}
|
disabled
|
||||||
options={[{ value: collection.name, label: compile(collection.title) }]}
|
value={collection.name}
|
||||||
/>
|
options={[{ value: collection.name, label: compile(collection.title) }]}
|
||||||
</div>
|
/>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'SourceCollection' },
|
||||||
|
);
|
||||||
|
|
||||||
export const SourceKey = observer(() => {
|
export const SourceKey = observer(
|
||||||
return (
|
() => {
|
||||||
<div>
|
return (
|
||||||
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
<div>
|
||||||
</div>
|
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'SourceKey' },
|
||||||
|
);
|
||||||
|
|
||||||
export const TargetKey = observer(() => {
|
export const TargetKey = observer(
|
||||||
return (
|
() => {
|
||||||
<div>
|
return (
|
||||||
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
<div>
|
||||||
</div>
|
<Select disabled value={'id'} options={[{ value: 'id', label: 'ID' }]} />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'TargetKey' },
|
||||||
|
);
|
||||||
|
@ -166,107 +166,113 @@ export const ds = {
|
|||||||
useDestroyAction,
|
useDestroyAction,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SubFieldDataSourceProvider = observer((props) => {
|
export const SubFieldDataSourceProvider = observer(
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
(props) => {
|
||||||
const [dataSource, setDataSource] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
const record = useRecord();
|
const [dataSource, setDataSource] = useState([]);
|
||||||
const api = useAPIClient();
|
const record = useRecord();
|
||||||
const service = useRequest(
|
const api = useAPIClient();
|
||||||
() => {
|
const service = useRequest(
|
||||||
if (record.interface === 'subTable') {
|
() => {
|
||||||
if (record?.children?.length) {
|
if (record.interface === 'subTable') {
|
||||||
return Promise.resolve({
|
if (record?.children?.length) {
|
||||||
data: record?.children || [],
|
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
|
return Promise.resolve({
|
||||||
.resource('fields')
|
data: [],
|
||||||
.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);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
);
|
onSuccess(data) {
|
||||||
return (
|
console.log('dataSource1', data?.data);
|
||||||
<CollectionProvider collection={collection}>
|
setDataSource(data?.data);
|
||||||
<DataSourceContext.Provider
|
},
|
||||||
value={{
|
},
|
||||||
rowKey: 'name',
|
);
|
||||||
service,
|
return (
|
||||||
dataSource,
|
<CollectionProvider collection={collection}>
|
||||||
setDataSource,
|
<DataSourceContext.Provider
|
||||||
selectedRowKeys,
|
value={{
|
||||||
setSelectedRowKeys,
|
rowKey: 'name',
|
||||||
}}
|
service,
|
||||||
>
|
dataSource,
|
||||||
{props.children}
|
setDataSource,
|
||||||
</DataSourceContext.Provider>
|
selectedRowKeys,
|
||||||
</CollectionProvider>
|
setSelectedRowKeys,
|
||||||
);
|
}}
|
||||||
});
|
>
|
||||||
|
{props.children}
|
||||||
|
</DataSourceContext.Provider>
|
||||||
|
</CollectionProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'SubFieldDataSourceProvider' },
|
||||||
|
);
|
||||||
|
|
||||||
export const DataSourceProvider = observer((props: any) => {
|
export const DataSourceProvider = observer(
|
||||||
const { rowKey, collection, association } = props;
|
(props: any) => {
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
const { rowKey, collection, association } = props;
|
||||||
const [dataSource, setDataSource] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
const record = useRecord();
|
const [dataSource, setDataSource] = useState([]);
|
||||||
const api = useAPIClient();
|
const record = useRecord();
|
||||||
const { getCollection } = useCollectionManager();
|
const api = useAPIClient();
|
||||||
const coll = getCollection(collection);
|
const { getCollection } = useCollectionManager();
|
||||||
const resourceOf = record?.[association.targetKey || 'id'];
|
const coll = getCollection(collection);
|
||||||
const service = useRequest(
|
const resourceOf = record?.[association.targetKey || 'id'];
|
||||||
() => {
|
const service = useRequest(
|
||||||
if (resourceOf) {
|
() => {
|
||||||
return api
|
if (resourceOf) {
|
||||||
.request({
|
return api
|
||||||
resource: `${association.collectionName}.${association.name}`,
|
.request({
|
||||||
resourceOf,
|
resource: `${association.collectionName}.${association.name}`,
|
||||||
action: 'list',
|
resourceOf,
|
||||||
params: {
|
action: 'list',
|
||||||
appends: coll?.fields?.filter((field) => field.target)?.map((field) => field.name),
|
params: {
|
||||||
},
|
appends: coll?.fields?.filter((field) => field.target)?.map((field) => field.name),
|
||||||
})
|
},
|
||||||
.then((res) => res.data);
|
})
|
||||||
}
|
.then((res) => res.data);
|
||||||
return Promise.resolve({
|
}
|
||||||
data: record?.[association.name] || [],
|
return Promise.resolve({
|
||||||
});
|
data: record?.[association.name] || [],
|
||||||
},
|
});
|
||||||
{
|
|
||||||
onSuccess(data) {
|
|
||||||
setDataSource(data?.data);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
);
|
onSuccess(data) {
|
||||||
return (
|
setDataSource(data?.data);
|
||||||
<CollectionProvider collection={coll}>
|
},
|
||||||
<DataSourceContext.Provider
|
},
|
||||||
value={{
|
);
|
||||||
rowKey,
|
return (
|
||||||
service,
|
<CollectionProvider collection={coll}>
|
||||||
dataSource,
|
<DataSourceContext.Provider
|
||||||
setDataSource,
|
value={{
|
||||||
selectedRowKeys,
|
rowKey,
|
||||||
setSelectedRowKeys,
|
service,
|
||||||
}}
|
dataSource,
|
||||||
>
|
setDataSource,
|
||||||
{props.children}
|
selectedRowKeys,
|
||||||
</DataSourceContext.Provider>
|
setSelectedRowKeys,
|
||||||
</CollectionProvider>
|
}}
|
||||||
);
|
>
|
||||||
});
|
{props.children}
|
||||||
|
</DataSourceContext.Provider>
|
||||||
|
</CollectionProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'DataSourceProvider' },
|
||||||
|
);
|
||||||
|
@ -217,3 +217,4 @@ function areEqual(prevProps, nextProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PreviewFields = React.memo(PreviewCom, areEqual);
|
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 { ActionPage } from './Action.Page';
|
||||||
import { ComposedActionDrawer } from './types';
|
import { ComposedActionDrawer } from './types';
|
||||||
|
|
||||||
export const ActionContainer: ComposedActionDrawer = observer((props: any) => {
|
export const ActionContainer: ComposedActionDrawer = observer(
|
||||||
const { openMode } = useActionContext();
|
(props: any) => {
|
||||||
if (openMode === 'drawer') {
|
const { openMode } = useActionContext();
|
||||||
return <ActionDrawer footerNodeName={'Action.Container.Footer'} {...props} />;
|
if (openMode === 'drawer') {
|
||||||
}
|
return <ActionDrawer footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||||
if (openMode === 'modal') {
|
}
|
||||||
return <ActionModal footerNodeName={'Action.Container.Footer'} {...props} />;
|
if (openMode === 'modal') {
|
||||||
}
|
return <ActionModal footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||||
return <ActionPage footerNodeName={'Action.Container.Footer'} {...props} />;
|
}
|
||||||
});
|
return <ActionPage footerNodeName={'Action.Container.Footer'} {...props} />;
|
||||||
|
},
|
||||||
|
{ displayName: 'ActionContainer' },
|
||||||
|
);
|
||||||
|
|
||||||
ActionContainer.Footer = observer(() => {
|
ActionContainer.Footer = observer(
|
||||||
const field = useField();
|
() => {
|
||||||
const schema = useFieldSchema();
|
const field = useField();
|
||||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
const schema = useFieldSchema();
|
||||||
});
|
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||||
|
},
|
||||||
|
{ displayName: 'ActionContainer.Footer' },
|
||||||
|
);
|
||||||
|
|
||||||
export default ActionContainer;
|
export default ActionContainer;
|
||||||
|
@ -14,111 +14,117 @@ const openSizeWidthMap = new Map<OpenSize, string>([
|
|||||||
['middle', '50%'],
|
['middle', '50%'],
|
||||||
['large', '70%'],
|
['large', '70%'],
|
||||||
]);
|
]);
|
||||||
export const ActionDrawer: ComposedActionDrawer = observer((props) => {
|
export const ActionDrawer: ComposedActionDrawer = observer(
|
||||||
const { footerNodeName = 'Action.Drawer.Footer', ...others } = props;
|
(props) => {
|
||||||
const { t } = useTranslation();
|
const { footerNodeName = 'Action.Drawer.Footer', ...others } = props;
|
||||||
const { visible, setVisible, openSize = 'middle' } = useActionContext();
|
const { t } = useTranslation();
|
||||||
const schema = useFieldSchema();
|
const { visible, setVisible, openSize = 'middle' } = useActionContext();
|
||||||
const field = useField();
|
const schema = useFieldSchema();
|
||||||
const openSizeFromParent = schema.parent?.['x-component-props']?.['openSize'];
|
const field = useField();
|
||||||
const finalOpenSize = openSizeFromParent || openSize;
|
const openSizeFromParent = schema.parent?.['x-component-props']?.['openSize'];
|
||||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
const finalOpenSize = openSizeFromParent || openSize;
|
||||||
if (s['x-component'] === footerNodeName) {
|
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||||
return s;
|
if (s['x-component'] === footerNodeName) {
|
||||||
}
|
return s;
|
||||||
return buf;
|
}
|
||||||
});
|
return buf;
|
||||||
return (
|
});
|
||||||
<>
|
return (
|
||||||
{createPortal(
|
<>
|
||||||
<div
|
{createPortal(
|
||||||
onClick={(e) => {
|
<div
|
||||||
e.stopPropagation();
|
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<RecursionField
|
<Drawer
|
||||||
basePath={field.address}
|
width={openSizeWidthMap.get(finalOpenSize)}
|
||||||
schema={schema}
|
title={field.title}
|
||||||
onlyRenderProperties
|
{...others}
|
||||||
filterProperties={(s) => {
|
destroyOnClose
|
||||||
return s['x-component'] !== footerNodeName;
|
open={visible}
|
||||||
}}
|
onClose={() => setVisible(false, true)}
|
||||||
/>
|
className={classNames(
|
||||||
</Drawer>
|
others.className,
|
||||||
</div>,
|
css`
|
||||||
document.body,
|
&.nb-action-popup {
|
||||||
)}
|
.ant-drawer-header {
|
||||||
</>
|
display: none;
|
||||||
);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
ActionDrawer.Footer = observer(() => {
|
.ant-drawer-body {
|
||||||
const field = useField();
|
padding-top: 14px;
|
||||||
const schema = useFieldSchema();
|
}
|
||||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
|
||||||
});
|
.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;
|
export default ActionDrawer;
|
||||||
|
@ -3,6 +3,9 @@ import React from 'react';
|
|||||||
import Action from './Action';
|
import Action from './Action';
|
||||||
import { ComposedAction } from './types';
|
import { ComposedAction } from './types';
|
||||||
|
|
||||||
export const ActionLink: ComposedAction = observer((props: any) => {
|
export const ActionLink: ComposedAction = observer(
|
||||||
return <Action {...props} component={'a'} className={'nb-action-link'} />;
|
(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%'],
|
['middle', '60%'],
|
||||||
['large', '80%'],
|
['large', '80%'],
|
||||||
]);
|
]);
|
||||||
export const ActionModal: ComposedActionDrawer<ModalProps> = observer((props) => {
|
export const ActionModal: ComposedActionDrawer<ModalProps> = observer(
|
||||||
const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props;
|
(props) => {
|
||||||
const { visible, setVisible, openSize = 'large' } = useActionContext();
|
const { footerNodeName = 'Action.Modal.Footer', width, ...others } = props;
|
||||||
const actualWidth = width ?? openSizeWidthMap.get(openSize);
|
const { visible, setVisible, openSize = 'large' } = useActionContext();
|
||||||
const schema = useFieldSchema();
|
const actualWidth = width ?? openSizeWidthMap.get(openSize);
|
||||||
const field = useField();
|
const schema = useFieldSchema();
|
||||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
const field = useField();
|
||||||
if (s['x-component'] === footerNodeName) {
|
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||||
return s;
|
if (s['x-component'] === footerNodeName) {
|
||||||
}
|
return s;
|
||||||
return buf;
|
}
|
||||||
});
|
return buf;
|
||||||
return (
|
});
|
||||||
<>
|
return (
|
||||||
{createPortal(
|
<>
|
||||||
<div
|
{createPortal(
|
||||||
onClick={(e) => {
|
<div
|
||||||
e.stopPropagation();
|
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<RecursionField
|
<Modal
|
||||||
basePath={field.address}
|
width={actualWidth}
|
||||||
schema={schema}
|
title={field.title}
|
||||||
onlyRenderProperties
|
{...(others as ModalProps)}
|
||||||
filterProperties={(s) => {
|
destroyOnClose
|
||||||
return s['x-component'] !== footerNodeName;
|
visible={visible}
|
||||||
}}
|
onCancel={() => setVisible(false, true)}
|
||||||
/>
|
className={classNames(
|
||||||
</Modal>
|
others.className,
|
||||||
</div>,
|
css`
|
||||||
document.body,
|
&.nb-action-popup {
|
||||||
)}
|
.ant-modal-header {
|
||||||
</>
|
display: none;
|
||||||
);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
ActionModal.Footer = observer(() => {
|
.ant-modal-body {
|
||||||
const field = useField();
|
padding-top: 16px;
|
||||||
const schema = useFieldSchema();
|
}
|
||||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
|
||||||
});
|
.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;
|
export default ActionModal;
|
||||||
|
@ -10,65 +10,71 @@ const useScope = (key: string) => {
|
|||||||
return scope[key];
|
return scope[key];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ActionPage: ComposedActionDrawer = observer((props: any) => {
|
export const ActionPage: ComposedActionDrawer = observer(
|
||||||
const { footerNodeName = 'Action.Page.Footer', ...others } = props;
|
(props: any) => {
|
||||||
const { containerRefKey, visible, setVisible } = useActionContext();
|
const { footerNodeName = 'Action.Page.Footer', ...others } = props;
|
||||||
const containerRef = useScope(containerRefKey);
|
const { containerRefKey, visible, setVisible } = useActionContext();
|
||||||
const schema = useFieldSchema();
|
const containerRef = useScope(containerRefKey);
|
||||||
const field = useField();
|
const schema = useFieldSchema();
|
||||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
const field = useField();
|
||||||
if (s['x-component'] === footerNodeName) {
|
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||||
return s;
|
if (s['x-component'] === footerNodeName) {
|
||||||
}
|
return s;
|
||||||
return buf;
|
}
|
||||||
});
|
return buf;
|
||||||
return (
|
});
|
||||||
<>
|
return (
|
||||||
{containerRef?.current &&
|
<>
|
||||||
visible &&
|
{containerRef?.current &&
|
||||||
createPortal(
|
visible &&
|
||||||
<div>
|
createPortal(
|
||||||
<RecursionField
|
<div>
|
||||||
basePath={field.address}
|
<RecursionField
|
||||||
schema={schema}
|
basePath={field.address}
|
||||||
onlyRenderProperties
|
schema={schema}
|
||||||
filterProperties={(s) => {
|
onlyRenderProperties
|
||||||
return s['x-component'] !== footerNodeName;
|
filterProperties={(s) => {
|
||||||
}}
|
return s['x-component'] !== footerNodeName;
|
||||||
/>
|
}}
|
||||||
{footerSchema && (
|
/>
|
||||||
<div
|
{footerSchema && (
|
||||||
className={css`
|
<div
|
||||||
display: flex;
|
className={css`
|
||||||
/* justify-content: flex-end; */
|
display: flex;
|
||||||
/* flex-direction: row-reverse; */
|
/* justify-content: flex-end; */
|
||||||
width: 100%;
|
/* flex-direction: row-reverse; */
|
||||||
.ant-btn {
|
width: 100%;
|
||||||
margin-right: 8px;
|
.ant-btn {
|
||||||
}
|
margin-right: 8px;
|
||||||
`}
|
}
|
||||||
>
|
`}
|
||||||
<RecursionField
|
>
|
||||||
basePath={field.address}
|
<RecursionField
|
||||||
schema={schema}
|
basePath={field.address}
|
||||||
onlyRenderProperties
|
schema={schema}
|
||||||
filterProperties={(s) => {
|
onlyRenderProperties
|
||||||
return s['x-component'] === footerNodeName;
|
filterProperties={(s) => {
|
||||||
}}
|
return s['x-component'] === footerNodeName;
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
)}
|
</div>
|
||||||
</div>,
|
)}
|
||||||
containerRef?.current,
|
</div>,
|
||||||
)}
|
containerRef?.current,
|
||||||
</>
|
)}
|
||||||
);
|
</>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'ActionPage' },
|
||||||
|
);
|
||||||
|
|
||||||
ActionPage.Footer = observer(() => {
|
ActionPage.Footer = observer(
|
||||||
const field = useField();
|
() => {
|
||||||
const schema = useFieldSchema();
|
const field = useField();
|
||||||
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
const schema = useFieldSchema();
|
||||||
});
|
return <RecursionField basePath={field.address} schema={schema} onlyRenderProperties />;
|
||||||
|
},
|
||||||
|
{ displayName: 'ActionPage.Footer' },
|
||||||
|
);
|
||||||
|
|
||||||
export default ActionPage;
|
export default ActionPage;
|
||||||
|
@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
|||||||
import { observer, RecursionField, useField, useFieldSchema, useForm } from '@formily/react';
|
import { observer, RecursionField, useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
import { Button, Modal, Popover } from 'antd';
|
import { Button, Modal, Popover } from 'antd';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useActionContext } from '../..';
|
import { useActionContext } from '../..';
|
||||||
import { useDesignable } from '../../';
|
import { useDesignable } from '../../';
|
||||||
import { Icon } from '../../../icon';
|
import { Icon } from '../../../icon';
|
||||||
@ -64,139 +64,158 @@ export const actionDesignerCss = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Action: ComposedAction = observer((props: any) => {
|
export const Action: ComposedAction = observer(
|
||||||
const {
|
(props: any) => {
|
||||||
popover,
|
const {
|
||||||
confirm,
|
popover,
|
||||||
openMode: om,
|
confirm,
|
||||||
containerRefKey,
|
openMode: om,
|
||||||
component,
|
containerRefKey,
|
||||||
useAction = useA,
|
component,
|
||||||
className,
|
useAction = useA,
|
||||||
icon,
|
className,
|
||||||
title,
|
icon,
|
||||||
...others
|
title,
|
||||||
} = props;
|
...others
|
||||||
const { onClick } = useProps(props);
|
} = props;
|
||||||
const [visible, setVisible] = useState(false);
|
const { onClick } = useProps(props);
|
||||||
const [formValueChanged, setFormValueChanged] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const Designer = useDesigner();
|
const [formValueChanged, setFormValueChanged] = useState(false);
|
||||||
const field = useField<any>();
|
const Designer = useDesigner();
|
||||||
const { run } = useAction();
|
const field = useField<any>();
|
||||||
const fieldSchema = useFieldSchema();
|
const { run } = useAction();
|
||||||
const compile = useCompile();
|
const fieldSchema = useFieldSchema();
|
||||||
const form = useForm();
|
const compile = useCompile();
|
||||||
const values = useRecord();
|
const form = useForm();
|
||||||
const designerProps = fieldSchema['x-designer-props'];
|
const values = useRecord();
|
||||||
const openMode = fieldSchema?.['x-component-props']?.['openMode'];
|
const designerProps = fieldSchema['x-designer-props'];
|
||||||
const disabled = form.disabled || field.disabled || props.disabled;
|
const openMode = fieldSchema?.['x-component-props']?.['openMode'];
|
||||||
const openSize = fieldSchema?.['x-component-props']?.['openSize'];
|
const disabled = form.disabled || field.disabled || props.disabled;
|
||||||
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
|
const openSize = fieldSchema?.['x-component-props']?.['openSize'];
|
||||||
const { designable } = useDesignable();
|
const linkageRules = fieldSchema?.['x-linkage-rules'] || [];
|
||||||
const tarComponent = useComponent(component) || component;
|
const { designable } = useDesignable();
|
||||||
useEffect(() => {
|
const tarComponent = useComponent(component) || component;
|
||||||
field.linkageProperty = {};
|
const renderTitle = useMemo(() => {
|
||||||
linkageRules
|
if (title) {
|
||||||
.filter((k) => !k.disabled)
|
return title;
|
||||||
.forEach((v) => {
|
}
|
||||||
return v.actions?.map((h) => {
|
if (fieldSchema.title) {
|
||||||
linkageAction(h.operator, field, v.condition, values);
|
return compile(fieldSchema.title);
|
||||||
});
|
}
|
||||||
});
|
|
||||||
}, [linkageRules, values, designable]);
|
|
||||||
|
|
||||||
const renderButton = () => {
|
|
||||||
if (!designable && field?.data?.hidden) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}, [compile, fieldSchema.title, title]);
|
||||||
return (
|
|
||||||
<SortableItem
|
useEffect(() => {
|
||||||
{...others}
|
field.linkageProperty = {};
|
||||||
loading={field?.data?.loading}
|
linkageRules
|
||||||
icon={<Icon type={icon} />}
|
.filter((k) => !k.disabled)
|
||||||
disabled={disabled}
|
.forEach((v) => {
|
||||||
style={{
|
return v.actions?.map((h) => {
|
||||||
opacity: designable && field?.data?.hidden && 0.1,
|
linkageAction(h.operator, field, v.condition, values);
|
||||||
}}
|
});
|
||||||
onClick={(e: React.MouseEvent) => {
|
});
|
||||||
if (!disabled) {
|
}, [linkageRules, values, designable]);
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
const renderButton = () => {
|
||||||
const onOk = () => {
|
if (!designable && field?.data?.hidden) {
|
||||||
onClick?.(e);
|
return null;
|
||||||
setVisible(true);
|
}
|
||||||
run();
|
return (
|
||||||
};
|
<SortableItem
|
||||||
if (confirm) {
|
{...others}
|
||||||
Modal.confirm({
|
loading={field?.data?.loading}
|
||||||
...confirm,
|
icon={<Icon type={icon} />}
|
||||||
onOk,
|
disabled={disabled}
|
||||||
});
|
style={{
|
||||||
} else {
|
opacity: designable && field?.data?.hidden && 0.1,
|
||||||
onOk();
|
}}
|
||||||
|
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)}
|
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
|
||||||
<Designer {...designerProps} />
|
{!popover && renderButton()}
|
||||||
</SortableItem>
|
{!popover && props.children}
|
||||||
|
</ActionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
{ displayName: 'Action' },
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
Action.Popover = observer(
|
||||||
<ActionContext.Provider
|
(props) => {
|
||||||
value={{
|
const { button, visible, setVisible } = useActionContext();
|
||||||
button: renderButton(),
|
return (
|
||||||
visible,
|
<Popover
|
||||||
setVisible,
|
{...props}
|
||||||
formValueChanged,
|
destroyTooltipOnHide
|
||||||
setFormValueChanged,
|
open={visible}
|
||||||
openMode,
|
onOpenChange={(visible) => {
|
||||||
openSize,
|
setVisible(visible);
|
||||||
containerRefKey,
|
}}
|
||||||
fieldSchema,
|
content={props.children}
|
||||||
}}
|
>
|
||||||
>
|
{button}
|
||||||
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
|
</Popover>
|
||||||
{!popover && renderButton()}
|
);
|
||||||
{!popover && props.children}
|
},
|
||||||
</ActionContext.Provider>
|
{ displayName: 'Action.Popover' },
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
Action.Popover = observer((props) => {
|
Action.Popover.Footer = observer(
|
||||||
const { button, visible, setVisible } = useActionContext();
|
(props) => {
|
||||||
return (
|
return (
|
||||||
<Popover
|
<div
|
||||||
{...props}
|
className={css`
|
||||||
destroyTooltipOnHide
|
display: flex;
|
||||||
open={visible}
|
justify-content: flex-end;
|
||||||
onOpenChange={(visible) => {
|
width: 100%;
|
||||||
setVisible(visible);
|
`}
|
||||||
}}
|
>
|
||||||
content={props.children}
|
{props.children}
|
||||||
>
|
</div>
|
||||||
{button}
|
);
|
||||||
</Popover>
|
},
|
||||||
);
|
{ displayName: 'Action.Popover.Footer' },
|
||||||
});
|
);
|
||||||
|
|
||||||
Action.Popover.Footer = observer((props) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
width: 100%;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Action.Link = ActionLink;
|
Action.Link = ActionLink;
|
||||||
Action.Designer = ActionDesigner;
|
Action.Designer = ActionDesigner;
|
||||||
|
@ -6,40 +6,41 @@ import { useSchemaInitializer } from '../../../schema-initializer';
|
|||||||
import { DndContext } from '../../common';
|
import { DndContext } from '../../common';
|
||||||
import { useDesignable, useProps } from '../../hooks';
|
import { useDesignable, useProps } from '../../hooks';
|
||||||
|
|
||||||
export const ActionBar = observer((props: any) => {
|
export const ActionBar = observer(
|
||||||
const { layout = 'tow-columns', style, spaceProps, ...others } = useProps(props);
|
(props: any) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const { layout = 'tow-columns', style, spaceProps, ...others } = useProps(props);
|
||||||
const { InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
const fieldSchema = useFieldSchema();
|
||||||
const { designable } = useDesignable();
|
const { InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||||
if (layout === 'one-column') {
|
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 (
|
return (
|
||||||
<DndContext>
|
<div
|
||||||
<div
|
style={
|
||||||
style={{ display: 'flex', alignItems: 'center', ...style }}
|
!designable && !hasActions
|
||||||
{...others}
|
? undefined
|
||||||
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
|
|
||||||
: {
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -47,38 +48,40 @@ export const ActionBar = observer((props: any) => {
|
|||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
...style,
|
...style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{...others}
|
{...others}
|
||||||
className={cx(others.className, 'nb-action-bar')}
|
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%' }}
|
|
||||||
>
|
>
|
||||||
<DndContext>
|
<div
|
||||||
<Space {...spaceProps}>
|
className={css`
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
.ant-space:last-child {
|
||||||
if (schema['x-align'] !== 'left') {
|
margin-left: 8px;
|
||||||
return null;
|
}
|
||||||
}
|
`}
|
||||||
return <RecursionField key={key} name={key} schema={schema} />;
|
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}
|
||||||
})}
|
>
|
||||||
</Space>
|
<DndContext>
|
||||||
<Space {...spaceProps}>
|
<Space {...spaceProps}>
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
{fieldSchema.mapProperties((schema, key) => {
|
||||||
if (schema['x-align'] === 'left') {
|
if (schema['x-align'] !== 'left') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <RecursionField key={key} name={key} schema={schema} />;
|
return <RecursionField key={key} name={key} schema={schema} />;
|
||||||
})}
|
})}
|
||||||
</Space>
|
</Space>
|
||||||
</DndContext>
|
<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>
|
</div>
|
||||||
<InitializerComponent />
|
);
|
||||||
</div>
|
},
|
||||||
);
|
{ displayName: 'ActionBar' },
|
||||||
});
|
);
|
||||||
|
@ -18,30 +18,33 @@ const useCloseAction = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const Editable = observer((props) => {
|
const Editable = observer(
|
||||||
const field = useField<Field>();
|
(props) => {
|
||||||
const schema = useFieldSchema();
|
const field = useField<Field>();
|
||||||
return (
|
const schema = useFieldSchema();
|
||||||
<div>
|
return (
|
||||||
<div style={{ marginBottom: 24 }}>
|
<div>
|
||||||
打开方式:
|
<div style={{ marginBottom: 24 }}>
|
||||||
<Select
|
打开方式:
|
||||||
value={field.componentProps.openMode}
|
<Select
|
||||||
style={{ width: 100 }}
|
value={field.componentProps.openMode}
|
||||||
onChange={(value) => {
|
style={{ width: 100 }}
|
||||||
field.componentProps.openMode = value;
|
onChange={(value) => {
|
||||||
schema['x-component-props']['openMode'] = 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={'drawer'}>Drawer</Select.Option>
|
||||||
<Select.Option value={'page'}>Page</Select.Option>
|
<Select.Option value={'modal'}>Modal</Select.Option>
|
||||||
</Select>
|
<Select.Option value={'page'}>Page</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
{props.children}
|
);
|
||||||
</div>
|
},
|
||||||
);
|
{ displayName: 'Editable' },
|
||||||
});
|
);
|
||||||
|
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -71,52 +71,55 @@ function TypeSelect(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditOptions = observer((props) => {
|
const EditOptions = observer(
|
||||||
const record = ArrayTable.useRecord();
|
(props) => {
|
||||||
const field = useField<Field>();
|
const record = ArrayTable.useRecord();
|
||||||
console.log(record.type);
|
const field = useField<Field>();
|
||||||
return (
|
console.log(record.type);
|
||||||
<div>
|
return (
|
||||||
<a
|
<div>
|
||||||
onClick={() => {
|
<a
|
||||||
FormDrawer('Pop-up form', () => {
|
onClick={() => {
|
||||||
return (
|
FormDrawer('Pop-up form', () => {
|
||||||
<FormLayout labelCol={6} wrapperCol={10}>
|
return (
|
||||||
<SchemaField
|
<FormLayout labelCol={6} wrapperCol={10}>
|
||||||
schema={{
|
<SchemaField
|
||||||
type: 'object',
|
schema={{
|
||||||
properties: record.type ? types[record.type] || {} : {},
|
type: 'object',
|
||||||
}}
|
properties: record.type ? types[record.type] || {} : {},
|
||||||
/>
|
}}
|
||||||
<FormDrawer.Footer>
|
/>
|
||||||
<FormButtonGroup align="right">
|
<FormDrawer.Footer>
|
||||||
<Submit
|
<FormButtonGroup align="right">
|
||||||
onSubmit={() => {
|
<Submit
|
||||||
return new Promise((resolve) => {
|
onSubmit={() => {
|
||||||
setTimeout(resolve, 1000);
|
return new Promise((resolve) => {
|
||||||
});
|
setTimeout(resolve, 1000);
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
Submit
|
>
|
||||||
</Submit>
|
Submit
|
||||||
</FormButtonGroup>
|
</Submit>
|
||||||
</FormDrawer.Footer>
|
</FormButtonGroup>
|
||||||
</FormLayout>
|
</FormDrawer.Footer>
|
||||||
);
|
</FormLayout>
|
||||||
})
|
);
|
||||||
.open({
|
|
||||||
initialValues: field.value,
|
|
||||||
})
|
})
|
||||||
.then((values) => {
|
.open({
|
||||||
field.value = values;
|
initialValues: field.value,
|
||||||
});
|
})
|
||||||
}}
|
.then((values) => {
|
||||||
>
|
field.value = values;
|
||||||
Edit
|
});
|
||||||
</a>
|
}}
|
||||||
</div>
|
>
|
||||||
);
|
Edit
|
||||||
});
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'EditOptions' },
|
||||||
|
);
|
||||||
|
|
||||||
const SchemaField = createSchemaField({
|
const SchemaField = createSchemaField({
|
||||||
components: {
|
components: {
|
||||||
|
@ -4,63 +4,66 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||||||
import { useCollectionManager } from '../../../collection-manager';
|
import { useCollectionManager } from '../../../collection-manager';
|
||||||
import { AssociationFieldContext } from './context';
|
import { AssociationFieldContext } from './context';
|
||||||
|
|
||||||
export const AssociationFieldProvider = observer((props) => {
|
export const AssociationFieldProvider = observer(
|
||||||
const field = useField<Field>();
|
(props) => {
|
||||||
const { getCollectionJoinField, getCollection } = useCollectionManager();
|
const field = useField<Field>();
|
||||||
const fieldSchema = useFieldSchema();
|
const { getCollectionJoinField, getCollection } = useCollectionManager();
|
||||||
const allowMultiple = fieldSchema['x-component-props']?.multiple !== false;
|
const fieldSchema = useFieldSchema();
|
||||||
const allowDissociate = fieldSchema['x-component-props']?.allowDissociate !== false;
|
const allowMultiple = fieldSchema['x-component-props']?.multiple !== false;
|
||||||
|
const allowDissociate = fieldSchema['x-component-props']?.allowDissociate !== false;
|
||||||
|
|
||||||
const collectionField = useMemo(
|
const collectionField = useMemo(
|
||||||
() => getCollectionJoinField(fieldSchema['x-collection-field']),
|
() => getCollectionJoinField(fieldSchema['x-collection-field']),
|
||||||
[fieldSchema['x-collection-field'], fieldSchema.name],
|
[fieldSchema['x-collection-field'], fieldSchema.name],
|
||||||
);
|
);
|
||||||
const isFileCollection = useMemo(
|
const isFileCollection = useMemo(
|
||||||
() => getCollection(collectionField?.target)?.template === 'file',
|
() => getCollection(collectionField?.target)?.template === 'file',
|
||||||
[fieldSchema['x-collection-field']],
|
[fieldSchema['x-collection-field']],
|
||||||
);
|
);
|
||||||
const currentMode = useMemo(
|
const currentMode = useMemo(
|
||||||
() => fieldSchema['x-component-props']?.mode || (isFileCollection ? 'FileManager' : 'Select'),
|
() => fieldSchema['x-component-props']?.mode || (isFileCollection ? 'FileManager' : 'Select'),
|
||||||
[fieldSchema['x-component-props']?.mode],
|
[fieldSchema['x-component-props']?.mode],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (!collectionField) {
|
if (!collectionField) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (field.value !== null && field.value !== undefined) {
|
if (field.value !== null && field.value !== undefined) {
|
||||||
// Nester 子表单时,如果没数据初始化一个 [null] 的占位
|
// Nester 子表单时,如果没数据初始化一个 [null] 的占位
|
||||||
if (currentMode === 'Nester' && Array.isArray(field.value)) {
|
if (currentMode === 'Nester' && Array.isArray(field.value)) {
|
||||||
if (field.value.length === 0 && ['belongsToMany', 'hasMany'].includes(collectionField.type)) {
|
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 = [{}];
|
field.value = [{}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
}, [currentMode, collectionField, field.value]);
|
||||||
}
|
|
||||||
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]);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectionField ? (
|
return collectionField ? (
|
||||||
<AssociationFieldContext.Provider
|
<AssociationFieldContext.Provider
|
||||||
value={{ options: collectionField, field, allowMultiple, allowDissociate, currentMode }}
|
value={{ options: collectionField, field, allowMultiple, allowDissociate, currentMode }}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</AssociationFieldContext.Provider>
|
</AssociationFieldContext.Provider>
|
||||||
) : null;
|
) : null;
|
||||||
});
|
},
|
||||||
|
{ displayName: 'AssociationFieldProvider' },
|
||||||
|
);
|
||||||
|
@ -11,41 +11,44 @@ export type AssociationSelectProps<P = any> = RemoteSelectProps<P> & {
|
|||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InternalAssociationSelect = observer((props: AssociationSelectProps) => {
|
const InternalAssociationSelect = observer(
|
||||||
const { fieldNames, objectValue = true } = props;
|
(props: AssociationSelectProps) => {
|
||||||
const field: any = useField();
|
const { fieldNames, objectValue = true } = props;
|
||||||
const fieldSchema = useFieldSchema();
|
const field: any = useField();
|
||||||
const service = useServiceOptions(props);
|
const fieldSchema = useFieldSchema();
|
||||||
const isAllowAddNew = fieldSchema['x-add-new'];
|
const service = useServiceOptions(props);
|
||||||
const value = Array.isArray(props.value) ? props.value.filter(Boolean) : props.value;
|
const isAllowAddNew = fieldSchema['x-add-new'];
|
||||||
|
const value = Array.isArray(props.value) ? props.value.filter(Boolean) : props.value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={fieldSchema.name}>
|
<div key={fieldSchema.name}>
|
||||||
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
||||||
<RemoteSelect
|
<RemoteSelect
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
{...props}
|
{...props}
|
||||||
objectValue={objectValue}
|
objectValue={objectValue}
|
||||||
value={value}
|
value={value}
|
||||||
service={service}
|
service={service}
|
||||||
></RemoteSelect>
|
></RemoteSelect>
|
||||||
|
|
||||||
{isAllowAddNew && (
|
{isAllowAddNew && (
|
||||||
<RecordProvider record={null}>
|
<RecordProvider record={null}>
|
||||||
<RecursionField
|
<RecursionField
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
return s['x-component'] === 'Action';
|
return s['x-component'] === 'Action';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</RecordProvider>
|
</RecordProvider>
|
||||||
)}
|
)}
|
||||||
</Input.Group>
|
</Input.Group>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'InternalAssociationSelect' },
|
||||||
|
);
|
||||||
|
|
||||||
interface AssociationSelectInterface {
|
interface AssociationSelectInterface {
|
||||||
(props: any): React.ReactElement;
|
(props: any): React.ReactElement;
|
||||||
|
@ -12,50 +12,56 @@ import { InternalSubTable } from './InternalSubTable';
|
|||||||
import { CreateRecordAction } from './components/CreateRecordAction';
|
import { CreateRecordAction } from './components/CreateRecordAction';
|
||||||
import { useAssociationFieldContext } from './hooks';
|
import { useAssociationFieldContext } from './hooks';
|
||||||
|
|
||||||
const EditableAssociationField = observer((props: any) => {
|
const EditableAssociationField = observer(
|
||||||
const { multiple } = props;
|
(props: any) => {
|
||||||
const field: Field = useField();
|
const { multiple } = props;
|
||||||
const form = useForm();
|
const field: Field = useField();
|
||||||
const fieldSchema = useFieldSchema();
|
const form = useForm();
|
||||||
const { options: collectionField, currentMode } = useAssociationFieldContext();
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { options: collectionField, currentMode } = useAssociationFieldContext();
|
||||||
|
|
||||||
const useCreateActionProps = () => {
|
const useCreateActionProps = () => {
|
||||||
const { onClick } = useCAP();
|
const { onClick } = useCAP();
|
||||||
const actionField: any = useField();
|
const actionField: any = useField();
|
||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
await onClick();
|
await onClick();
|
||||||
const { data } = actionField.data?.data?.data || {};
|
const { data } = actionField.data?.data?.data || {};
|
||||||
if (data) {
|
if (data) {
|
||||||
if (['m2m', 'o2m'].includes(collectionField?.interface) && multiple !== false) {
|
if (['m2m', 'o2m'].includes(collectionField?.interface) && multiple !== false) {
|
||||||
const values = form.getValuesIn(field.path) || [];
|
const values = form.getValuesIn(field.path) || [];
|
||||||
values.push(data);
|
values.push(data);
|
||||||
form.setValuesIn(field.path, values);
|
form.setValuesIn(field.path, values);
|
||||||
field.onInput(values);
|
field.onInput(values);
|
||||||
} else {
|
} else {
|
||||||
form.setValuesIn(field.path, data);
|
form.setValuesIn(field.path, data);
|
||||||
field.onInput(data);
|
field.onInput(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentOptions scope={{ useCreateActionProps }} components={{ CreateRecordAction }}>
|
<SchemaComponentOptions scope={{ useCreateActionProps }} components={{ CreateRecordAction }}>
|
||||||
{currentMode === 'Picker' && <InternalPicker {...props} />}
|
{currentMode === 'Picker' && <InternalPicker {...props} />}
|
||||||
{currentMode === 'Nester' && <InternalNester {...props} />}
|
{currentMode === 'Nester' && <InternalNester {...props} />}
|
||||||
{currentMode === 'Select' && <AssociationSelect {...props} />}
|
{currentMode === 'Select' && <AssociationSelect {...props} />}
|
||||||
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
||||||
{currentMode === 'FileManager' && <InternalFileManager {...props} />}
|
{currentMode === 'FileManager' && <InternalFileManager {...props} />}
|
||||||
</SchemaComponentOptions>
|
</SchemaComponentOptions>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'EditableAssociationField' },
|
||||||
|
);
|
||||||
|
|
||||||
export const Editable = observer((props) => {
|
export const Editable = observer(
|
||||||
return (
|
(props) => {
|
||||||
<AssociationFieldProvider>
|
return (
|
||||||
<EditableAssociationField {...props} />
|
<AssociationFieldProvider>
|
||||||
</AssociationFieldProvider>
|
<EditableAssociationField {...props} />
|
||||||
);
|
</AssociationFieldProvider>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Editable' },
|
||||||
|
);
|
||||||
|
@ -60,138 +60,145 @@ const useTableSelectorProps = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InternalPicker = observer((props: any) => {
|
export const InternalPicker = observer(
|
||||||
const { value, multiple, onChange, quickUpload, selectFile, ...others } = props;
|
(props: any) => {
|
||||||
const field: any = useField();
|
const { value, multiple, onChange, quickUpload, selectFile, ...others } = props;
|
||||||
const fieldNames = useFieldNames(props);
|
const field: any = useField();
|
||||||
const [visibleSelector, setVisibleSelector] = useState(false);
|
const fieldNames = useFieldNames(props);
|
||||||
const fieldSchema = useFieldSchema();
|
const [visibleSelector, setVisibleSelector] = useState(false);
|
||||||
const insertSelector = useInsertSchema('Selector');
|
const fieldSchema = useFieldSchema();
|
||||||
const { options: collectionField } = useAssociationFieldContext();
|
const insertSelector = useInsertSchema('Selector');
|
||||||
const compile = useCompile();
|
const { options: collectionField } = useAssociationFieldContext();
|
||||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
const compile = useCompile();
|
||||||
const isAllowAddNew = fieldSchema['x-add-new'];
|
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
const isAllowAddNew = fieldSchema['x-add-new'];
|
||||||
const options = useMemo(() => {
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
if (value && Object.keys(value).length > 0) {
|
const options = useMemo(() => {
|
||||||
const opts = (Array.isArray(value) ? value : value ? [value] : []).filter(Boolean).map((option) => {
|
if (value && Object.keys(value).length > 0) {
|
||||||
const label = option?.[fieldNames.label];
|
const opts = (Array.isArray(value) ? value : value ? [value] : []).filter(Boolean).map((option) => {
|
||||||
return {
|
const label = option?.[fieldNames.label];
|
||||||
...option,
|
return {
|
||||||
[fieldNames.label]: getLabelFormatValue(compile(labelUiSchema), compile(label)),
|
...option,
|
||||||
};
|
[fieldNames.label]: getLabelFormatValue(compile(labelUiSchema), compile(label)),
|
||||||
});
|
};
|
||||||
return opts;
|
});
|
||||||
}
|
return opts;
|
||||||
return [];
|
}
|
||||||
}, [value, fieldNames?.label]);
|
return [];
|
||||||
|
}, [value, fieldNames?.label]);
|
||||||
|
|
||||||
const pickerProps = {
|
const pickerProps = {
|
||||||
size: 'small',
|
size: 'small',
|
||||||
fieldNames,
|
fieldNames,
|
||||||
multiple: multiple !== false && ['o2m', 'm2m'].includes(collectionField?.interface),
|
multiple: multiple !== false && ['o2m', 'm2m'].includes(collectionField?.interface),
|
||||||
association: {
|
association: {
|
||||||
target: collectionField?.target,
|
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);
|
|
||||||
},
|
},
|
||||||
|
options,
|
||||||
|
onChange: props?.onChange,
|
||||||
|
selectedRows,
|
||||||
|
setSelectedRows,
|
||||||
|
collectionField,
|
||||||
};
|
};
|
||||||
};
|
|
||||||
return (
|
const getValue = () => {
|
||||||
<>
|
if (multiple == null) return null;
|
||||||
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
return Array.isArray(value)
|
||||||
<div style={{ width: '100%' }}>
|
? value.filter(Boolean)?.map((v) => v?.[fieldNames.value])
|
||||||
<Select
|
: value?.[fieldNames.value];
|
||||||
style={{ width: '100%' }}
|
};
|
||||||
{...others}
|
const getFilter = () => {
|
||||||
mode={multiple ? 'multiple' : props.mode}
|
const targetKey = collectionField?.targetKey || 'id';
|
||||||
fieldNames={fieldNames}
|
const list = options.map((option) => option[targetKey]).filter(Boolean);
|
||||||
onDropdownVisibleChange={(open) => {
|
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
|
||||||
insertSelector(schema.Selector);
|
return filter;
|
||||||
setVisibleSelector(true);
|
};
|
||||||
}}
|
const usePickActionProps = () => {
|
||||||
allowClear
|
const { setVisible } = useActionContext();
|
||||||
onChange={(changed: any) => {
|
const { multiple, selectedRows, onChange, options, collectionField } = useContext(RecordPickerContext);
|
||||||
if (!changed) {
|
return {
|
||||||
const value = multiple ? [] : null;
|
onClick() {
|
||||||
onChange(value);
|
if (multiple) {
|
||||||
setSelectedRows(value);
|
onChange(unionBy(selectedRows, options, collectionField?.targetKey || 'id'));
|
||||||
} else if (Array.isArray(changed)) {
|
} else {
|
||||||
if (!changed.length) {
|
onChange(selectedRows?.[0] || null);
|
||||||
onChange([]);
|
}
|
||||||
setSelectedRows([]);
|
setVisible(false);
|
||||||
return;
|
},
|
||||||
}
|
};
|
||||||
const values = options?.filter((option) => changed.includes(option[fieldNames.value]));
|
};
|
||||||
onChange(values);
|
return (
|
||||||
setSelectedRows(values);
|
<>
|
||||||
}
|
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
|
||||||
}}
|
<div style={{ width: '100%' }}>
|
||||||
options={options}
|
<Select
|
||||||
value={getValue()}
|
style={{ width: '100%' }}
|
||||||
open={false}
|
{...others}
|
||||||
/>
|
mode={multiple ? 'multiple' : props.mode}
|
||||||
</div>
|
fieldNames={fieldNames}
|
||||||
{isAllowAddNew && (
|
onDropdownVisibleChange={(open) => {
|
||||||
<RecordProvider record={null}>
|
insertSelector(schema.Selector);
|
||||||
<RecursionField
|
setVisibleSelector(true);
|
||||||
onlyRenderProperties
|
|
||||||
basePath={field.address}
|
|
||||||
schema={fieldSchema}
|
|
||||||
filterProperties={(s) => {
|
|
||||||
return s['x-component'] === 'Action';
|
|
||||||
}}
|
}}
|
||||||
|
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>
|
</div>
|
||||||
)}
|
{isAllowAddNew && (
|
||||||
</Input.Group>
|
<RecordProvider record={null}>
|
||||||
<ActionContext.Provider value={{ openMode: 'drawer', visible: visibleSelector, setVisible: setVisibleSelector }}>
|
<RecursionField
|
||||||
<RecordPickerProvider {...pickerProps}>
|
onlyRenderProperties
|
||||||
<CollectionProvider name={collectionField?.target}>
|
basePath={field.address}
|
||||||
<FormProvider>
|
schema={fieldSchema}
|
||||||
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
|
filterProperties={(s) => {
|
||||||
<SchemaComponentOptions scope={{ usePickActionProps, useTableSelectorProps }}>
|
return s['x-component'] === 'Action';
|
||||||
<RecursionField
|
}}
|
||||||
onlyRenderProperties
|
/>
|
||||||
basePath={field.address}
|
</RecordProvider>
|
||||||
schema={fieldSchema}
|
)}
|
||||||
filterProperties={(s) => {
|
</Input.Group>
|
||||||
return s['x-component'] === 'AssociationField.Selector';
|
<ActionContext.Provider
|
||||||
}}
|
value={{ openMode: 'drawer', visible: visibleSelector, setVisible: setVisibleSelector }}
|
||||||
/>
|
>
|
||||||
</SchemaComponentOptions>
|
<RecordPickerProvider {...pickerProps}>
|
||||||
</TableSelectorParamsProvider>
|
<CollectionProvider name={collectionField?.target}>
|
||||||
</FormProvider>
|
<FormProvider>
|
||||||
</CollectionProvider>
|
<TableSelectorParamsProvider params={{ filter: getFilter() }}>
|
||||||
</RecordPickerProvider>
|
<SchemaComponentOptions scope={{ usePickActionProps, useTableSelectorProps }}>
|
||||||
</ActionContext.Provider>
|
<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;
|
return value;
|
||||||
};
|
};
|
||||||
export const ReadPrettyInternalViewer: React.FC = observer((props: any) => {
|
export const ReadPrettyInternalViewer: React.FC = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props: any) => {
|
||||||
const recordCtx = useRecord();
|
const fieldSchema = useFieldSchema();
|
||||||
const { enableLink } = fieldSchema['x-component-props'];
|
const recordCtx = useRecord();
|
||||||
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
const { enableLink } = fieldSchema['x-component-props'];
|
||||||
const field = useField();
|
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
||||||
const fieldNames = useFieldNames(props);
|
const field = useField();
|
||||||
const [visible, setVisible] = useState(false);
|
const fieldNames = useFieldNames(props);
|
||||||
const insertViewer = useInsertSchema('Viewer');
|
const [visible, setVisible] = useState(false);
|
||||||
const { options: collectionField } = useAssociationFieldContext();
|
const insertViewer = useInsertSchema('Viewer');
|
||||||
const [record, setRecord] = useState({});
|
const { options: collectionField } = useAssociationFieldContext();
|
||||||
const compile = useCompile();
|
const [record, setRecord] = useState({});
|
||||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
const compile = useCompile();
|
||||||
const { snapshot } = useActionContext();
|
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
const { snapshot } = useActionContext();
|
||||||
|
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||||
|
|
||||||
const renderRecords = () =>
|
const renderRecords = () =>
|
||||||
toArr(props.value).map((record, index, arr) => {
|
toArr(props.value).map((record, index, arr) => {
|
||||||
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
||||||
const text = getLabelFormatValue(compile(labelUiSchema), val, true);
|
const text = getLabelFormatValue(compile(labelUiSchema), val, true);
|
||||||
return (
|
return (
|
||||||
<Fragment key={`${record.id}_${index}`}>
|
<Fragment key={`${record.id}_${index}`}>
|
||||||
<span>
|
<span>
|
||||||
{snapshot ? (
|
{snapshot ? (
|
||||||
text
|
text
|
||||||
) : enableLink !== false ? (
|
) : enableLink !== false ? (
|
||||||
<a
|
<a
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
insertViewer(schema.Viewer);
|
insertViewer(schema.Viewer);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
setRecord(record);
|
setRecord(record);
|
||||||
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
text
|
text
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderWithoutTableFieldResourceProvider = () => (
|
const renderWithoutTableFieldResourceProvider = () => (
|
||||||
<WithoutTableFieldResource.Provider value={true}>
|
<WithoutTableFieldResource.Provider value={true}>
|
||||||
<FormProvider>
|
<FormProvider>
|
||||||
<RecursionField
|
<RecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
basePath={field.address}
|
basePath={field.address}
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
return s['x-component'] === 'AssociationField.Viewer';
|
return s['x-component'] === 'AssociationField.Viewer';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</WithoutTableFieldResource.Provider>
|
</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>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const renderRecordProvider = () => {
|
||||||
<div>
|
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||||
<BlockAssociationContext.Provider value={`${collectionField?.collectionName}.${collectionField?.name}`}>
|
|
||||||
<CollectionProvider name={collectionField?.target ?? collectionField?.targetCollection}>
|
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||||
<EllipsisWithTooltip ellipsis={true} ref={ellipsisWithTooltipRef}>
|
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||||
{renderRecords()}
|
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||||
</EllipsisWithTooltip>
|
</RecordProvider>
|
||||||
<ActionContext.Provider
|
) : (
|
||||||
value={{ visible, setVisible, openMode: 'drawer', snapshot: collectionField?.interface === 'snapshot' }}
|
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||||
>
|
);
|
||||||
{renderRecordProvider()}
|
};
|
||||||
</ActionContext.Provider>
|
|
||||||
</CollectionProvider>
|
return (
|
||||||
</BlockAssociationContext.Provider>
|
<div>
|
||||||
</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>;
|
return <Card bordered={true}>{props.children}</Card>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ToManyNester = observer((props) => {
|
const ToManyNester = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props) => {
|
||||||
const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext<ArrayField>();
|
const fieldSchema = useFieldSchema();
|
||||||
const { t } = useTranslation();
|
const { options, field, allowMultiple, allowDissociate } = useAssociationFieldContext<ArrayField>();
|
||||||
return (field.value || []).length > 0 ? (
|
const { t } = useTranslation();
|
||||||
<Card
|
return (field.value || []).length > 0 ? (
|
||||||
bordered={true}
|
<Card
|
||||||
style={{ position: 'relative' }}
|
bordered={true}
|
||||||
className={css`
|
style={{ position: 'relative' }}
|
||||||
> .ant-card-body > .ant-divider:last-child {
|
className={css`
|
||||||
display: none;
|
> .ant-card-body > .ant-divider:last-child {
|
||||||
}
|
display: none;
|
||||||
`}
|
}
|
||||||
>
|
`}
|
||||||
{(field.value || []).map((value, index) => {
|
>
|
||||||
let allowed = allowDissociate;
|
{(field.value || []).map((value, index) => {
|
||||||
if (!allowDissociate) {
|
let allowed = allowDissociate;
|
||||||
allowed = !value?.[options.targetKey];
|
if (!allowDissociate) {
|
||||||
}
|
allowed = !value?.[options.targetKey];
|
||||||
return (
|
}
|
||||||
<>
|
return (
|
||||||
<div style={{ textAlign: 'right' }}>
|
<>
|
||||||
{field.editable && allowMultiple && (
|
<div style={{ textAlign: 'right' }}>
|
||||||
<Tooltip key={'add'} title={t('Add new')}>
|
{field.editable && allowMultiple && (
|
||||||
<PlusOutlined
|
<Tooltip key={'add'} title={t('Add new')}>
|
||||||
style={{ zIndex: 1000, marginRight: '10px', color: '#a8a3a3' }}
|
<PlusOutlined
|
||||||
onClick={() => {
|
style={{ zIndex: 1000, marginRight: '10px', color: '#a8a3a3' }}
|
||||||
action(() => {
|
onClick={() => {
|
||||||
if (!Array.isArray(field.value)) {
|
action(() => {
|
||||||
field.value = [];
|
if (!Array.isArray(field.value)) {
|
||||||
}
|
field.value = [];
|
||||||
spliceArrayState(field as any, {
|
}
|
||||||
startIndex: index + 1,
|
spliceArrayState(field as any, {
|
||||||
insertCount: 1,
|
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>
|
<Tooltip key={'remove'} title={t('Remove')}>
|
||||||
)}
|
<CloseOutlined
|
||||||
{!field.readPretty && allowed && (
|
style={{ zIndex: 1000, color: '#a8a3a3' }}
|
||||||
<Tooltip key={'remove'} title={t('Remove')}>
|
onClick={() => {
|
||||||
<CloseOutlined
|
action(() => {
|
||||||
style={{ zIndex: 1000, color: '#a8a3a3' }}
|
spliceArrayState(field as any, {
|
||||||
onClick={() => {
|
startIndex: index,
|
||||||
action(() => {
|
deleteCount: 1,
|
||||||
spliceArrayState(field as any, {
|
});
|
||||||
startIndex: index,
|
field.value.splice(index, 1);
|
||||||
deleteCount: 1,
|
return field.onInput(field.value);
|
||||||
});
|
});
|
||||||
field.value.splice(index, 1);
|
}}
|
||||||
return field.onInput(field.value);
|
/>
|
||||||
});
|
</Tooltip>
|
||||||
}}
|
)}
|
||||||
/>
|
</div>
|
||||||
</Tooltip>
|
<RecursionField onlyRenderProperties basePath={field.address.concat(index)} schema={fieldSchema} />
|
||||||
)}
|
<Divider />
|
||||||
</div>
|
</>
|
||||||
<RecursionField onlyRenderProperties basePath={field.address.concat(index)} schema={fieldSchema} />
|
);
|
||||||
<Divider />
|
})}
|
||||||
</>
|
</Card>
|
||||||
);
|
) : (
|
||||||
})}
|
<>
|
||||||
</Card>
|
{field.editable && allowMultiple && (
|
||||||
) : (
|
<Tooltip key={'add'} title={t('Add new')}>
|
||||||
<>
|
<Button
|
||||||
{field.editable && allowMultiple && (
|
type={'default'}
|
||||||
<Tooltip key={'add'} title={t('Add new')}>
|
className={css`
|
||||||
<Button
|
border: 1px solid #f0f0f0 !important;
|
||||||
type={'default'}
|
box-shadow: none;
|
||||||
className={css`
|
`}
|
||||||
border: 1px solid #f0f0f0 !important;
|
block
|
||||||
box-shadow: none;
|
icon={<PlusOutlined />}
|
||||||
`}
|
onClick={() => {
|
||||||
block
|
const result = field.value;
|
||||||
icon={<PlusOutlined />}
|
result.push({});
|
||||||
onClick={() => {
|
field.value = result;
|
||||||
const result = field.value;
|
}}
|
||||||
result.push({});
|
></Button>
|
||||||
field.value = result;
|
</Tooltip>
|
||||||
}}
|
)}
|
||||||
></Button>
|
</>
|
||||||
</Tooltip>
|
);
|
||||||
)}
|
},
|
||||||
</>
|
{ displayName: 'ToManyNester' },
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
@ -7,23 +7,29 @@ import { InternalSubTable } from './InternalSubTable';
|
|||||||
import { FileManageReadPretty } from './FileManager';
|
import { FileManageReadPretty } from './FileManager';
|
||||||
import { useAssociationFieldContext } from './hooks';
|
import { useAssociationFieldContext } from './hooks';
|
||||||
|
|
||||||
const ReadPrettyAssociationField = observer((props: any) => {
|
const ReadPrettyAssociationField = observer(
|
||||||
const { currentMode } = useAssociationFieldContext();
|
(props: any) => {
|
||||||
|
const { currentMode } = useAssociationFieldContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{['Select', 'Picker'].includes(currentMode) && <ReadPrettyInternalViewer {...props} />}
|
{['Select', 'Picker'].includes(currentMode) && <ReadPrettyInternalViewer {...props} />}
|
||||||
{currentMode === 'Nester' && <InternalNester {...props} />}
|
{currentMode === 'Nester' && <InternalNester {...props} />}
|
||||||
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
{currentMode === 'SubTable' && <InternalSubTable {...props} />}
|
||||||
{currentMode === 'FileManager' && <FileManageReadPretty {...props} />}
|
{currentMode === 'FileManager' && <FileManageReadPretty {...props} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'ReadPrettyAssociationField' },
|
||||||
|
);
|
||||||
|
|
||||||
export const ReadPretty = observer((props) => {
|
export const ReadPretty = observer(
|
||||||
return (
|
(props) => {
|
||||||
<AssociationFieldProvider>
|
return (
|
||||||
<ReadPrettyAssociationField {...props} />
|
<AssociationFieldProvider>
|
||||||
</AssociationFieldProvider>
|
<ReadPrettyAssociationField {...props} />
|
||||||
);
|
</AssociationFieldProvider>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'ReadPretty' },
|
||||||
|
);
|
||||||
|
@ -5,24 +5,27 @@ import React from 'react';
|
|||||||
import { Table } from '../table-v2/Table';
|
import { Table } from '../table-v2/Table';
|
||||||
import { useAssociationFieldContext } from './hooks';
|
import { useAssociationFieldContext } from './hooks';
|
||||||
|
|
||||||
export const SubTable: any = observer((props) => {
|
export const SubTable: any = observer(
|
||||||
const { field } = useAssociationFieldContext<ArrayField>();
|
(props) => {
|
||||||
return (
|
const { field } = useAssociationFieldContext<ArrayField>();
|
||||||
<div>
|
return (
|
||||||
<Table size={'small'} showIndex field={field} pagination={false} />
|
<div>
|
||||||
{field.editable && (
|
<Table size={'small'} showIndex field={field} pagination={false} />
|
||||||
<Button
|
{field.editable && (
|
||||||
type={'dashed'}
|
<Button
|
||||||
block
|
type={'dashed'}
|
||||||
style={{ marginTop: 12 }}
|
block
|
||||||
onClick={() => {
|
style={{ marginTop: 12 }}
|
||||||
field.value = field.value || [];
|
onClick={() => {
|
||||||
field.value.push({});
|
field.value = field.value || [];
|
||||||
}}
|
field.value.push({});
|
||||||
>
|
}}
|
||||||
Add new
|
>
|
||||||
</Button>
|
Add new
|
||||||
)}
|
</Button>
|
||||||
</div>
|
)}
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'SubTable' },
|
||||||
|
);
|
||||||
|
@ -6,34 +6,37 @@ import { ActionContext, useActionContext } from '../../action';
|
|||||||
import { useAssociationFieldContext, useInsertSchema } from '../hooks';
|
import { useAssociationFieldContext, useInsertSchema } from '../hooks';
|
||||||
import schema from '../schema';
|
import schema from '../schema';
|
||||||
|
|
||||||
export const CreateRecordAction = observer((props) => {
|
export const CreateRecordAction = observer(
|
||||||
const field: any = useField();
|
(props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const field: any = useField();
|
||||||
const ctx = useActionContext();
|
const fieldSchema = useFieldSchema();
|
||||||
const insertAddNewer = useInsertSchema('AddNewer');
|
const ctx = useActionContext();
|
||||||
const { options: collectionField } = useAssociationFieldContext();
|
const insertAddNewer = useInsertSchema('AddNewer');
|
||||||
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
|
const { options: collectionField } = useAssociationFieldContext();
|
||||||
const [currentCollection, setCurrentCollection] = useState(collectionField?.target);
|
const [visibleAddNewer, setVisibleAddNewer] = useState(false);
|
||||||
const addbuttonClick = (name) => {
|
const [currentCollection, setCurrentCollection] = useState(collectionField?.target);
|
||||||
insertAddNewer(schema.AddNewer);
|
const addbuttonClick = (name) => {
|
||||||
setVisibleAddNewer(true);
|
insertAddNewer(schema.AddNewer);
|
||||||
setCurrentCollection(name);
|
setVisibleAddNewer(true);
|
||||||
};
|
setCurrentCollection(name);
|
||||||
return (
|
};
|
||||||
<CollectionProvider name={collectionField?.target}>
|
return (
|
||||||
<CreateAction {...props} onClick={(arg) => addbuttonClick(arg)} />
|
<CollectionProvider name={collectionField?.target}>
|
||||||
<ActionContext.Provider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
|
<CreateAction {...props} onClick={(arg) => addbuttonClick(arg)} />
|
||||||
<CollectionProvider name={currentCollection}>
|
<ActionContext.Provider value={{ ...ctx, visible: visibleAddNewer, setVisible: setVisibleAddNewer }}>
|
||||||
<RecursionField
|
<CollectionProvider name={currentCollection}>
|
||||||
onlyRenderProperties
|
<RecursionField
|
||||||
basePath={field.address}
|
onlyRenderProperties
|
||||||
schema={fieldSchema}
|
basePath={field.address}
|
||||||
filterProperties={(s) => {
|
schema={fieldSchema}
|
||||||
return s['x-component'] === 'AssociationField.AddNewer';
|
filterProperties={(s) => {
|
||||||
}}
|
return s['x-component'] === 'AssociationField.AddNewer';
|
||||||
/>
|
}}
|
||||||
</CollectionProvider>
|
/>
|
||||||
</ActionContext.Provider>
|
</CollectionProvider>
|
||||||
</CollectionProvider>
|
</ActionContext.Provider>
|
||||||
);
|
</CollectionProvider>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'CreateRecordAction' },
|
||||||
|
);
|
||||||
|
@ -4,8 +4,11 @@ import { useFieldTitle } from '../../hooks';
|
|||||||
import { RemoteSelect } from '../remote-select';
|
import { RemoteSelect } from '../remote-select';
|
||||||
import useServiceOptions from './useServiceOptions';
|
import useServiceOptions from './useServiceOptions';
|
||||||
|
|
||||||
export const ReadPretty = observer((props: any) => {
|
export const ReadPretty = observer(
|
||||||
const service = useServiceOptions(props);
|
(props: any) => {
|
||||||
useFieldTitle();
|
const service = useServiceOptions(props);
|
||||||
return <RemoteSelect.ReadPretty {...props} service={service}></RemoteSelect.ReadPretty>;
|
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 { BlockItem, DndContext, DragHandler, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Block = observer((props) => {
|
const Block = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props) => {
|
||||||
return (
|
const fieldSchema = useFieldSchema();
|
||||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
return (
|
||||||
Block {fieldSchema.name}
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
<DragHandler />
|
Block {fieldSchema.name}
|
||||||
</div>
|
<DragHandler />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Block' },
|
||||||
|
);
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -190,71 +190,74 @@ const CalendarRecordViewer = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Calendar: any = observer((props: any) => {
|
export const Calendar: any = observer(
|
||||||
const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props);
|
(props: any) => {
|
||||||
const [date, setDate] = useState<Date>(new Date());
|
const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props);
|
||||||
const [view, setView] = useState<(typeof Weeks)[number]>('month');
|
const [date, setDate] = useState<Date>(new Date());
|
||||||
const events = useEvents(dataSource, fieldNames, date, view);
|
const [view, setView] = useState<(typeof Weeks)[number]>('month');
|
||||||
const [visible, setVisible] = useState(false);
|
const events = useEvents(dataSource, fieldNames, date, view);
|
||||||
const [record, setRecord] = useState<any>({});
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [record, setRecord] = useState<any>({});
|
||||||
|
|
||||||
const components = useMemo(() => {
|
const components = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
toolbar: (props) => <Toolbar {...props} showLunar={showLunar}></Toolbar>,
|
toolbar: (props) => <Toolbar {...props} showLunar={showLunar}></Toolbar>,
|
||||||
week: {
|
week: {
|
||||||
header: (props) => <Header {...props} type="week" showLunar={showLunar}></Header>,
|
header: (props) => <Header {...props} type="week" showLunar={showLunar}></Header>,
|
||||||
},
|
},
|
||||||
month: {
|
month: {
|
||||||
dateHeader: (props) => <Header {...props} showLunar={showLunar}></Header>,
|
dateHeader: (props) => <Header {...props} showLunar={showLunar}></Header>,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, [showLunar]);
|
}, [showLunar]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: fixedBlock ? '100%' : 700 }}>
|
<div style={{ height: fixedBlock ? '100%' : 700 }}>
|
||||||
<CalendarRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
<CalendarRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
||||||
<BigCalendar
|
<BigCalendar
|
||||||
popup
|
popup
|
||||||
selectable
|
selectable
|
||||||
events={events}
|
events={events}
|
||||||
view={view}
|
view={view}
|
||||||
views={Weeks}
|
views={Weeks}
|
||||||
date={date}
|
date={date}
|
||||||
step={60}
|
step={60}
|
||||||
showMultiDayTimes
|
showMultiDayTimes
|
||||||
messages={messages}
|
messages={messages}
|
||||||
onNavigate={setDate}
|
onNavigate={setDate}
|
||||||
onView={setView}
|
onView={setView}
|
||||||
onSelectSlot={(slotInfo) => {
|
onSelectSlot={(slotInfo) => {
|
||||||
console.log('onSelectSlot', slotInfo);
|
console.log('onSelectSlot', slotInfo);
|
||||||
}}
|
}}
|
||||||
onDoubleClickEvent={(event) => {
|
onDoubleClickEvent={(event) => {
|
||||||
console.log('onDoubleClickEvent');
|
console.log('onDoubleClickEvent');
|
||||||
}}
|
}}
|
||||||
onSelectEvent={(event) => {
|
onSelectEvent={(event) => {
|
||||||
const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
|
const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return;
|
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 `${local.format(start, 'Y-M', culture)} - ${local.format(end, 'Y-M', culture)}`;
|
record.__event = { ...event, start: formatDate(moment(event.start)), end: formatDate(moment(event.end)) };
|
||||||
},
|
|
||||||
}}
|
setRecord(record);
|
||||||
components={components}
|
setVisible(true);
|
||||||
localizer={localizer}
|
}}
|
||||||
/>
|
formats={{
|
||||||
</div>
|
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';
|
import { formatDate } from './utils';
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
export const DeleteEvent = observer(() => {
|
export const DeleteEvent = observer(
|
||||||
const { visible, setVisible } = useActionContext();
|
() => {
|
||||||
const { exclude = [], cron, ...record } = useRecord();
|
const { visible, setVisible } = useActionContext();
|
||||||
const { close } = useContext(DeleteEventContext);
|
const { exclude = [], cron, ...record } = useRecord();
|
||||||
const startDate = formatDate(moment(record.__parent.__event.start));
|
const { close } = useContext(DeleteEventContext);
|
||||||
const filterByTk = useFilterByTk();
|
const startDate = formatDate(moment(record.__parent.__event.start));
|
||||||
const { resource, service, __parent } = useBlockRequestContext();
|
const filterByTk = useFilterByTk();
|
||||||
const [value, onChange] = useState(startDate);
|
const { resource, service, __parent } = useBlockRequestContext();
|
||||||
const [loading, setLoading] = useState(false);
|
const [value, onChange] = useState(startDate);
|
||||||
const onOk = async () => {
|
const [loading, setLoading] = useState(false);
|
||||||
setLoading(true);
|
const onOk = async () => {
|
||||||
if (value === 'all' || !cron) {
|
setLoading(true);
|
||||||
await resource.destroy({
|
if (value === 'all' || !cron) {
|
||||||
filterByTk,
|
await resource.destroy({
|
||||||
});
|
filterByTk,
|
||||||
} else {
|
});
|
||||||
await resource.update({
|
} else {
|
||||||
filterByTk,
|
await resource.update({
|
||||||
values: {
|
filterByTk,
|
||||||
exclude: (exclude || []).concat(value),
|
values: {
|
||||||
},
|
exclude: (exclude || []).concat(value),
|
||||||
});
|
},
|
||||||
}
|
});
|
||||||
setLoading(false);
|
}
|
||||||
__parent?.service?.refresh?.();
|
setLoading(false);
|
||||||
service?.refresh?.();
|
__parent?.service?.refresh?.();
|
||||||
setVisible?.(false, true);
|
service?.refresh?.();
|
||||||
close();
|
setVisible?.(false, true);
|
||||||
};
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<Modal
|
<Modal
|
||||||
title={cron ? t('Delete events') : null}
|
title={cron ? t('Delete events') : null}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onCancel={() => setVisible(false)}
|
onCancel={() => setVisible(false)}
|
||||||
onOk={() => onOk()}
|
onOk={() => onOk()}
|
||||||
confirmLoading={loading}
|
confirmLoading={loading}
|
||||||
>
|
>
|
||||||
{cron ? (
|
{cron ? (
|
||||||
<Radio.Group value={value} onChange={(event) => onChange(event.target.value)}>
|
<Radio.Group value={value} onChange={(event) => onChange(event.target.value)}>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<Radio value={startDate}>{t('This event')}</Radio>
|
<Radio value={startDate}>{t('This event')}</Radio>
|
||||||
<Radio value={`${startDate}_after`}>{t('This and following events')}</Radio>
|
<Radio value={`${startDate}_after`}>{t('This and following events')}</Radio>
|
||||||
<Radio value="all">{t('All events')}</Radio>
|
<Radio value="all">{t('All events')}</Radio>
|
||||||
</Space>
|
</Space>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
) : (
|
) : (
|
||||||
<Text strong style={{ fontSize: '18px' }}>
|
<Text strong style={{ fontSize: '18px' }}>
|
||||||
{t('Delete this event?')}
|
{t('Delete this event?')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Modal>,
|
</Modal>,
|
||||||
document.body,
|
document.body,
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'DeleteEvent' },
|
||||||
|
);
|
||||||
|
|
||||||
export default DeleteEvent;
|
export default DeleteEvent;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { observer } from '@formily/react';
|
import { observer } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export const Event = observer((props) => {
|
export const Event = observer(
|
||||||
return <>{props.children}</>;
|
(props) => {
|
||||||
});
|
return <>{props.children}</>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Event' },
|
||||||
|
);
|
||||||
|
@ -6,14 +6,17 @@ import { navigate } from 'react-big-calendar/lib/utils/constants';
|
|||||||
import { useDesignable } from '../../hooks';
|
import { useDesignable } from '../../hooks';
|
||||||
import { CalendarToolbarContext } from './context';
|
import { CalendarToolbarContext } from './context';
|
||||||
|
|
||||||
export const Nav = observer((props) => {
|
export const Nav = observer(
|
||||||
const { DesignableBar } = useDesignable();
|
(props) => {
|
||||||
const { onNavigate } = useContext(CalendarToolbarContext);
|
const { DesignableBar } = useDesignable();
|
||||||
return (
|
const { onNavigate } = useContext(CalendarToolbarContext);
|
||||||
<div className="ant-btn-group">
|
return (
|
||||||
<Button icon={<LeftOutlined />} onClick={() => onNavigate(navigate.PREVIOUS)}></Button>
|
<div className="ant-btn-group">
|
||||||
<Button icon={<RightOutlined />} onClick={() => onNavigate(navigate.NEXT)}></Button>
|
<Button icon={<LeftOutlined />} onClick={() => onNavigate(navigate.PREVIOUS)}></Button>
|
||||||
<DesignableBar />
|
<Button icon={<RightOutlined />} onClick={() => onNavigate(navigate.NEXT)}></Button>
|
||||||
</div>
|
<DesignableBar />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Nav' },
|
||||||
|
);
|
||||||
|
@ -4,22 +4,25 @@ import { useDesignable } from '../../hooks';
|
|||||||
import { CalendarToolbarContext } from './context';
|
import { CalendarToolbarContext } from './context';
|
||||||
import { getLunarDay } from './utils';
|
import { getLunarDay } from './utils';
|
||||||
|
|
||||||
export const Title = observer(() => {
|
export const Title = observer(
|
||||||
const { DesignableBar } = useDesignable();
|
() => {
|
||||||
const { date, view, label, showLunar } = useContext(CalendarToolbarContext);
|
const { DesignableBar } = useDesignable();
|
||||||
|
const { date, view, label, showLunar } = useContext(CalendarToolbarContext);
|
||||||
|
|
||||||
const lunarElement = useMemo(() => {
|
const lunarElement = useMemo(() => {
|
||||||
if (!showLunar || view !== 'day') {
|
if (!showLunar || view !== 'day') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return <span>{getLunarDay(date)}</span>;
|
return <span>{getLunarDay(date)}</span>;
|
||||||
}, [view, date, showLunar]);
|
}, [view, date, showLunar]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ant-btn-group" style={{ fontSize: '1.75em', fontWeight: 300 }}>
|
<div className="ant-btn-group" style={{ fontSize: '1.75em', fontWeight: 300 }}>
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
<span style={{ marginLeft: '4px' }}>{lunarElement}</span>
|
<span style={{ marginLeft: '4px' }}>{lunarElement}</span>
|
||||||
<DesignableBar />
|
<DesignableBar />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'Title' },
|
||||||
|
);
|
||||||
|
@ -6,18 +6,21 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useDesignable } from '../../hooks';
|
import { useDesignable } from '../../hooks';
|
||||||
import { CalendarToolbarContext } from './context';
|
import { CalendarToolbarContext } from './context';
|
||||||
|
|
||||||
export const Today = observer((props) => {
|
export const Today = observer(
|
||||||
const { DesignableBar } = useDesignable();
|
(props) => {
|
||||||
const { onNavigate } = useContext(CalendarToolbarContext);
|
const { DesignableBar } = useDesignable();
|
||||||
const { t } = useTranslation();
|
const { onNavigate } = useContext(CalendarToolbarContext);
|
||||||
return (
|
const { t } = useTranslation();
|
||||||
<Button
|
return (
|
||||||
onClick={() => {
|
<Button
|
||||||
onNavigate(navigate.TODAY);
|
onClick={() => {
|
||||||
}}
|
onNavigate(navigate.TODAY);
|
||||||
>
|
}}
|
||||||
{t('Today')}
|
>
|
||||||
<DesignableBar />
|
{t('Today')}
|
||||||
</Button>
|
<DesignableBar />
|
||||||
);
|
</Button>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Today' },
|
||||||
|
);
|
||||||
|
@ -4,22 +4,25 @@ import React, { useContext } from 'react';
|
|||||||
import { useDesignable } from '../../hooks';
|
import { useDesignable } from '../../hooks';
|
||||||
import { CalendarToolbarContext } from './context';
|
import { CalendarToolbarContext } from './context';
|
||||||
|
|
||||||
export const ViewSelect = observer((props) => {
|
export const ViewSelect = observer(
|
||||||
const { DesignableBar } = useDesignable();
|
(props) => {
|
||||||
const {
|
const { DesignableBar } = useDesignable();
|
||||||
views,
|
const {
|
||||||
view,
|
views,
|
||||||
onView,
|
view,
|
||||||
localizer: { messages },
|
onView,
|
||||||
} = useContext(CalendarToolbarContext);
|
localizer: { messages },
|
||||||
return (
|
} = useContext(CalendarToolbarContext);
|
||||||
<div className="ant-btn-group">
|
return (
|
||||||
<Select value={view} onChange={onView}>
|
<div className="ant-btn-group">
|
||||||
{views.map((name) => (
|
<Select value={view} onChange={onView}>
|
||||||
<Select.Option value={name}>{messages[name]}</Select.Option>
|
{views.map((name) => (
|
||||||
))}
|
<Select.Option value={name}>{messages[name]}</Select.Option>
|
||||||
</Select>
|
))}
|
||||||
<DesignableBar />
|
</Select>
|
||||||
</div>
|
<DesignableBar />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'ViewSelect' },
|
||||||
|
);
|
||||||
|
@ -58,6 +58,6 @@ export const CollectionSelect = connect(
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}),
|
}, { displayName: 'CollectionSelectObserver' }),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -15,35 +15,38 @@ const useDef = (options) => {
|
|||||||
return useRequest(() => Promise.resolve({ data: field.dataSource }), options);
|
return useRequest(() => Promise.resolve({ data: field.dataSource }), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Filter: any = observer((props: any) => {
|
export const Filter: any = observer(
|
||||||
const { useDataSource = useDef } = props;
|
(props: any) => {
|
||||||
const { options, dynamicComponent, className } = useProps(props);
|
const { useDataSource = useDef } = props;
|
||||||
const field = useField<ObjectFieldModel>();
|
const { options, dynamicComponent, className } = useProps(props);
|
||||||
const fieldSchema = useFieldSchema();
|
const field = useField<ObjectFieldModel>();
|
||||||
useDataSource({
|
const fieldSchema = useFieldSchema();
|
||||||
onSuccess(data) {
|
useDataSource({
|
||||||
field.dataSource = data?.data || [];
|
onSuccess(data) {
|
||||||
},
|
field.dataSource = data?.data || [];
|
||||||
});
|
},
|
||||||
return (
|
});
|
||||||
<div className={className}>
|
return (
|
||||||
<DatePickerProvider value={{ utc: false }}>
|
<div className={className}>
|
||||||
<FilterContext.Provider
|
<DatePickerProvider value={{ utc: false }}>
|
||||||
value={{
|
<FilterContext.Provider
|
||||||
field,
|
value={{
|
||||||
fieldSchema,
|
field,
|
||||||
dynamicComponent,
|
fieldSchema,
|
||||||
options: options || field.dataSource || [],
|
dynamicComponent,
|
||||||
disabled: props.disabled,
|
options: options || field.dataSource || [],
|
||||||
}}
|
disabled: props.disabled,
|
||||||
>
|
}}
|
||||||
<FilterGroup {...props} bordered={false} />
|
>
|
||||||
{/* <pre>{JSON.stringify(field.value, null, 2)}</pre> */}
|
<FilterGroup {...props} bordered={false} />
|
||||||
</FilterContext.Provider>
|
{/* <pre>{JSON.stringify(field.value, null, 2)}</pre> */}
|
||||||
</DatePickerProvider>
|
</FilterContext.Provider>
|
||||||
</div>
|
</DatePickerProvider>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Filter' },
|
||||||
|
);
|
||||||
|
|
||||||
Filter.SaveDefaultValue = SaveDefaultValue;
|
Filter.SaveDefaultValue = SaveDefaultValue;
|
||||||
|
|
||||||
|
@ -11,83 +11,86 @@ import { Action } from '../action';
|
|||||||
|
|
||||||
export const FilterActionContext = createContext<any>(null);
|
export const FilterActionContext = createContext<any>(null);
|
||||||
|
|
||||||
export const FilterAction = observer((props: any) => {
|
export const FilterAction = observer(
|
||||||
const { t } = useTranslation();
|
(props: any) => {
|
||||||
const field = useField<Field>();
|
const { t } = useTranslation();
|
||||||
const [visible, setVisible] = useState(false);
|
const field = useField<Field>();
|
||||||
const { designable, dn } = useDesignable();
|
const [visible, setVisible] = useState(false);
|
||||||
const fieldSchema = useFieldSchema();
|
const { designable, dn } = useDesignable();
|
||||||
const form = useMemo<Form>(() => props.form || createForm(), []);
|
const fieldSchema = useFieldSchema();
|
||||||
const { options, onSubmit, onReset, ...others } = useProps(props);
|
const form = useMemo<Form>(() => props.form || createForm(), []);
|
||||||
return (
|
const { options, onSubmit, onReset, ...others } = useProps(props);
|
||||||
<FilterActionContext.Provider value={{ field, fieldSchema, designable, dn }}>
|
return (
|
||||||
<Popover
|
<FilterActionContext.Provider value={{ field, fieldSchema, designable, dn }}>
|
||||||
destroyTooltipOnHide
|
<Popover
|
||||||
placement={'bottomLeft'}
|
destroyTooltipOnHide
|
||||||
open={visible}
|
placement={'bottomLeft'}
|
||||||
onOpenChange={(visible) => {
|
open={visible}
|
||||||
setVisible(visible);
|
onOpenChange={(visible) => {
|
||||||
}}
|
setVisible(visible);
|
||||||
trigger={'click'}
|
}}
|
||||||
content={
|
trigger={'click'}
|
||||||
<form>
|
content={
|
||||||
<FormProvider form={form}>
|
<form>
|
||||||
<SchemaComponent
|
<FormProvider form={form}>
|
||||||
schema={{
|
<SchemaComponent
|
||||||
type: 'object',
|
schema={{
|
||||||
properties: {
|
type: 'object',
|
||||||
filter: {
|
properties: {
|
||||||
type: 'string',
|
filter: {
|
||||||
enum: options || field.dataSource,
|
type: 'string',
|
||||||
default: fieldSchema.default,
|
enum: options || field.dataSource,
|
||||||
'x-component': 'Filter',
|
default: fieldSchema.default,
|
||||||
'x-component-props': {},
|
'x-component': 'Filter',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<div
|
||||||
<div
|
className={css`
|
||||||
className={css`
|
display: flex;
|
||||||
display: flex;
|
justify-content: flex-end;
|
||||||
justify-content: flex-end;
|
width: 100%;
|
||||||
width: 100%;
|
`}
|
||||||
`}
|
>
|
||||||
>
|
<Space>
|
||||||
<Space>
|
<SaveConditions />
|
||||||
<SaveConditions />
|
<Button
|
||||||
<Button
|
onClick={async () => {
|
||||||
onClick={async () => {
|
await form.reset();
|
||||||
await form.reset();
|
onReset?.(form.values);
|
||||||
onReset?.(form.values);
|
field.title = t('Filter');
|
||||||
field.title = t('Filter');
|
setVisible(false);
|
||||||
setVisible(false);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{t('Reset')}
|
||||||
{t('Reset')}
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
<Button
|
type={'primary'}
|
||||||
type={'primary'}
|
htmlType={'submit'}
|
||||||
htmlType={'submit'}
|
onClick={(e) => {
|
||||||
onClick={(e) => {
|
e.preventDefault();
|
||||||
e.preventDefault();
|
e.stopPropagation();
|
||||||
e.stopPropagation();
|
onSubmit?.(form.values);
|
||||||
onSubmit?.(form.values);
|
setVisible(false);
|
||||||
setVisible(false);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{t('Submit')}
|
||||||
{t('Submit')}
|
</Button>
|
||||||
</Button>
|
</Space>
|
||||||
</Space>
|
</div>
|
||||||
</div>
|
</FormProvider>
|
||||||
</FormProvider>
|
</form>
|
||||||
</form>
|
}
|
||||||
}
|
>
|
||||||
>
|
<Action {...others} title={field.title} />
|
||||||
<Action {...others} title={field.title} />
|
</Popover>
|
||||||
</Popover>
|
</FilterActionContext.Provider>
|
||||||
</FilterActionContext.Provider>
|
);
|
||||||
);
|
},
|
||||||
});
|
{ displayName: 'FilterAction' },
|
||||||
|
);
|
||||||
|
|
||||||
const SaveConditions = () => {
|
const SaveConditions = () => {
|
||||||
const { fieldSchema, field, designable, dn } = useContext(FilterActionContext);
|
const { fieldSchema, field, designable, dn } = useContext(FilterActionContext);
|
||||||
|
@ -9,56 +9,59 @@ import { RemoveConditionContext } from './context';
|
|||||||
import { DynamicComponent } from './DynamicComponent';
|
import { DynamicComponent } from './DynamicComponent';
|
||||||
import { useValues } from './useValues';
|
import { useValues } from './useValues';
|
||||||
|
|
||||||
export const FilterItem = observer((props: any) => {
|
export const FilterItem = observer(
|
||||||
const { t } = useTranslation();
|
(props: any) => {
|
||||||
const compile = useCompile();
|
const { t } = useTranslation();
|
||||||
const remove = useContext(RemoveConditionContext);
|
const compile = useCompile();
|
||||||
const { schema, fields, operators, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues();
|
const remove = useContext(RemoveConditionContext);
|
||||||
return (
|
const { schema, fields, operators, dataIndex, operator, setDataIndex, setOperator, value, setValue } = useValues();
|
||||||
<div style={{ marginBottom: 8 }}>
|
return (
|
||||||
<Space>
|
<div style={{ marginBottom: 8 }}>
|
||||||
<Cascader
|
<Space>
|
||||||
className={css`
|
<Cascader
|
||||||
width: 160px;
|
className={css`
|
||||||
`}
|
width: 160px;
|
||||||
fieldNames={{
|
`}
|
||||||
label: 'title',
|
fieldNames={{
|
||||||
value: 'name',
|
label: 'title',
|
||||||
children: 'children',
|
value: 'name',
|
||||||
}}
|
children: 'children',
|
||||||
changeOnSelect={false}
|
}}
|
||||||
value={dataIndex}
|
changeOnSelect={false}
|
||||||
options={compile(fields)}
|
value={dataIndex}
|
||||||
onChange={(value) => {
|
options={compile(fields)}
|
||||||
setDataIndex(value);
|
onChange={(value) => {
|
||||||
}}
|
setDataIndex(value);
|
||||||
placeholder={t('Select Field')}
|
}}
|
||||||
/>
|
placeholder={t('Select Field')}
|
||||||
<Select
|
/>
|
||||||
className={css`
|
<Select
|
||||||
min-width: 110px;
|
className={css`
|
||||||
`}
|
min-width: 110px;
|
||||||
value={operator?.value}
|
`}
|
||||||
options={compile(operators)}
|
value={operator?.value}
|
||||||
onChange={(value) => {
|
options={compile(operators)}
|
||||||
setOperator(value);
|
onChange={(value) => {
|
||||||
}}
|
setOperator(value);
|
||||||
placeholder={t('Comparision')}
|
}}
|
||||||
/>
|
placeholder={t('Comparision')}
|
||||||
{!operator?.noValue &&
|
/>
|
||||||
React.createElement(DynamicComponent, {
|
{!operator?.noValue &&
|
||||||
value: value ? value : undefined,
|
React.createElement(DynamicComponent, {
|
||||||
schema,
|
value: value ? value : undefined,
|
||||||
onChange(value) {
|
schema,
|
||||||
setValue(value);
|
onChange(value) {
|
||||||
},
|
setValue(value);
|
||||||
})}
|
},
|
||||||
{!props.disabled && (
|
})}
|
||||||
<a>
|
{!props.disabled && (
|
||||||
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
|
<a>
|
||||||
</a>
|
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
|
||||||
)}
|
</a>
|
||||||
</Space>
|
)}
|
||||||
</div>
|
</Space>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'FilterItem' },
|
||||||
|
);
|
||||||
|
@ -5,17 +5,20 @@ import { RemoveConditionContext } from './context';
|
|||||||
import { FilterGroup } from './FilterGroup';
|
import { FilterGroup } from './FilterGroup';
|
||||||
import { FilterItem } from './FilterItem';
|
import { FilterItem } from './FilterItem';
|
||||||
|
|
||||||
export const FilterItems = observer((props) => {
|
export const FilterItems = observer(
|
||||||
const field = useField<ArrayFieldModel>();
|
(props) => {
|
||||||
return (
|
const field = useField<ArrayFieldModel>();
|
||||||
<div>
|
return (
|
||||||
{field?.value?.map((item, index) => {
|
<div>
|
||||||
return (
|
{field?.value?.map((item, index) => {
|
||||||
<RemoveConditionContext.Provider key={index} value={() => field.remove(index)}>
|
return (
|
||||||
<ObjectField name={index} component={[item.$and || item.$or ? FilterGroup : FilterItem]} />
|
<RemoveConditionContext.Provider key={index} value={() => field.remove(index)}>
|
||||||
</RemoveConditionContext.Provider>
|
<ObjectField name={index} component={[item.$and || item.$or ? FilterGroup : FilterItem]} />
|
||||||
);
|
</RemoveConditionContext.Provider>
|
||||||
})}
|
);
|
||||||
</div>
|
})}
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'FilterItems' },
|
||||||
|
);
|
||||||
|
@ -53,61 +53,64 @@ const divWrap = (schema: ISchema) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormItem: any = observer((props: any) => {
|
export const FormItem: any = observer(
|
||||||
useEnsureOperatorsValid();
|
(props: any) => {
|
||||||
|
useEnsureOperatorsValid();
|
||||||
|
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const ctx = useContext(BlockRequestContext);
|
const ctx = useContext(BlockRequestContext);
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const variablesCtx = useVariablesCtx();
|
const variablesCtx = useVariablesCtx();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ctx?.block === 'form') {
|
if (ctx?.block === 'form') {
|
||||||
ctx.field.data = ctx.field.data || {};
|
ctx.field.data = ctx.field.data || {};
|
||||||
ctx.field.data.activeFields = ctx.field.data.activeFields || new Set();
|
ctx.field.data.activeFields = ctx.field.data.activeFields || new Set();
|
||||||
ctx.field.data.activeFields.add(schema.name);
|
ctx.field.data.activeFields.add(schema.name);
|
||||||
// 如果默认值是一个变量,则需要解析之后再显示出来
|
// 如果默认值是一个变量,则需要解析之后再显示出来
|
||||||
if (isVariable(schema?.default)) {
|
if (isVariable(schema?.default)) {
|
||||||
field.setInitialValue?.(parseVariables(schema.default, variablesCtx));
|
field.setInitialValue?.(parseVariables(schema.default, variablesCtx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}, []);
|
||||||
}, []);
|
const showTitle = schema['x-decorator-props']?.showTitle ?? true;
|
||||||
const showTitle = schema['x-decorator-props']?.showTitle ?? true;
|
return (
|
||||||
return (
|
<ACLCollectionFieldProvider>
|
||||||
<ACLCollectionFieldProvider>
|
<BlockItem className={'nb-form-item'}>
|
||||||
<BlockItem className={'nb-form-item'}>
|
<Item
|
||||||
<Item
|
className={cx(
|
||||||
className={cx(
|
css`
|
||||||
css`
|
& .ant-space {
|
||||||
& .ant-space {
|
flex-wrap: wrap;
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
[css`
|
|
||||||
> .ant-formily-item-label {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
`]: showTitle === false,
|
`,
|
||||||
},
|
{
|
||||||
)}
|
[css`
|
||||||
{...props}
|
> .ant-formily-item-label {
|
||||||
extra={
|
display: none;
|
||||||
typeof field.description === 'string' ? (
|
}
|
||||||
<div
|
`]: showTitle === false,
|
||||||
dangerouslySetInnerHTML={{
|
},
|
||||||
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
|
)}
|
||||||
}}
|
{...props}
|
||||||
/>
|
extra={
|
||||||
) : (
|
typeof field.description === 'string' ? (
|
||||||
field.description
|
<div
|
||||||
)
|
dangerouslySetInnerHTML={{
|
||||||
}
|
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
|
||||||
/>
|
}}
|
||||||
</BlockItem>
|
/>
|
||||||
</ACLCollectionFieldProvider>
|
) : (
|
||||||
);
|
field.description
|
||||||
});
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</BlockItem>
|
||||||
|
</ACLCollectionFieldProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'FormItem' },
|
||||||
|
);
|
||||||
|
|
||||||
FormItem.Designer = function Designer() {
|
FormItem.Designer = function Designer() {
|
||||||
const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
|
const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
|
||||||
|
@ -170,21 +170,24 @@ export const Form: React.FC<FormProps> & {
|
|||||||
FilterDesigner?: any;
|
FilterDesigner?: any;
|
||||||
ReadPrettyDesigner?: any;
|
ReadPrettyDesigner?: any;
|
||||||
Templates?: any;
|
Templates?: any;
|
||||||
} = observer((props) => {
|
} = observer(
|
||||||
const field = useField<Field>();
|
(props) => {
|
||||||
const { form, disabled, ...others } = useProps(props);
|
const field = useField<Field>();
|
||||||
const formDisabled = disabled || field.disabled;
|
const { form, disabled, ...others } = useProps(props);
|
||||||
return (
|
const formDisabled = disabled || field.disabled;
|
||||||
<ConfigProvider componentDisabled={formDisabled}>
|
return (
|
||||||
<form>
|
<ConfigProvider componentDisabled={formDisabled}>
|
||||||
<Spin spinning={field.loading || false}>
|
<form>
|
||||||
{form ? (
|
<Spin spinning={field.loading || false}>
|
||||||
<WithForm form={form} {...others} disabled={formDisabled} />
|
{form ? (
|
||||||
) : (
|
<WithForm form={form} {...others} disabled={formDisabled} />
|
||||||
<WithoutForm {...others} disabled={formDisabled} />
|
) : (
|
||||||
)}
|
<WithoutForm {...others} disabled={formDisabled} />
|
||||||
</Spin>
|
)}
|
||||||
</form>
|
</Spin>
|
||||||
</ConfigProvider>
|
</form>
|
||||||
);
|
</ConfigProvider>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Form' },
|
||||||
|
);
|
||||||
|
@ -4,21 +4,24 @@ import { useFormBlockContext } from '../../../block-provider';
|
|||||||
import { useCollection } from '../../../collection-manager';
|
import { useCollection } from '../../../collection-manager';
|
||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
|
|
||||||
export const FormField: any = observer((props) => {
|
export const FormField: any = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props) => {
|
||||||
const { getField } = useCollection();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField();
|
const { getField } = useCollection();
|
||||||
const collectionField = getField(fieldSchema.name);
|
const field = useField();
|
||||||
const compile = useCompile();
|
const collectionField = getField(fieldSchema.name);
|
||||||
const ctx = useFormBlockContext();
|
const compile = useCompile();
|
||||||
useEffect(() => {
|
const ctx = useFormBlockContext();
|
||||||
if (!field.title) {
|
useEffect(() => {
|
||||||
field.title = compile(collectionField?.uiSchema?.title);
|
if (!field.title) {
|
||||||
}
|
field.title = compile(collectionField?.uiSchema?.title);
|
||||||
if (ctx?.field) {
|
}
|
||||||
ctx.field.added = ctx.field.added || new Set();
|
if (ctx?.field) {
|
||||||
ctx.field.added.add(fieldSchema.name);
|
ctx.field.added = ctx.field.added || new Set();
|
||||||
}
|
ctx.field.added.add(fieldSchema.name);
|
||||||
}, []);
|
}
|
||||||
return <div>{props.children}</div>;
|
}, []);
|
||||||
});
|
return <div>{props.children}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'FormField' },
|
||||||
|
);
|
||||||
|
@ -83,34 +83,37 @@ const useDefaultValues = (opts: any = {}, props: FormProps = {}) => {
|
|||||||
|
|
||||||
const FormBlockContext = createContext<any>(null);
|
const FormBlockContext = createContext<any>(null);
|
||||||
|
|
||||||
export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) => {
|
export const Form: React.FC<FormProps> & { Designer?: any } = observer(
|
||||||
const { request, effects, initialValue, useValues = useDefaultValues, ...others } = props;
|
(props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const { request, effects, initialValue, useValues = useDefaultValues, ...others } = props;
|
||||||
const field = useField();
|
const fieldSchema = useFieldSchema();
|
||||||
const form = useMemo(() => createForm({ effects }), []);
|
const field = useField();
|
||||||
const result = useValues(
|
const form = useMemo(() => createForm({ effects }), []);
|
||||||
{
|
const result = useValues(
|
||||||
uid: fieldSchema['x-uid'],
|
{
|
||||||
async onSuccess(data) {
|
uid: fieldSchema['x-uid'],
|
||||||
await form.reset();
|
async onSuccess(data) {
|
||||||
form.setValues(data?.data);
|
await form.reset();
|
||||||
|
form.setValues(data?.data);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
props,
|
||||||
props,
|
);
|
||||||
);
|
const parent = useContext(FormBlockContext);
|
||||||
const parent = useContext(FormBlockContext);
|
return (
|
||||||
return (
|
<FormBlockContext.Provider value={{ parent, form, result, field, fieldSchema }}>
|
||||||
<FormBlockContext.Provider value={{ parent, form, result, field, fieldSchema }}>
|
<Spin spinning={result?.loading || false}>
|
||||||
<Spin spinning={result?.loading || false}>
|
{fieldSchema['x-decorator'] === 'Form' ? (
|
||||||
{fieldSchema['x-decorator'] === 'Form' ? (
|
<FormDecorator form={form} {...others} />
|
||||||
<FormDecorator form={form} {...others} />
|
) : (
|
||||||
) : (
|
<FormComponent form={form} {...others} />
|
||||||
<FormComponent form={form} {...others} />
|
)}
|
||||||
)}
|
</Spin>
|
||||||
</Spin>
|
</FormBlockContext.Provider>
|
||||||
</FormBlockContext.Provider>
|
);
|
||||||
);
|
},
|
||||||
});
|
{ displayName: 'Form' },
|
||||||
|
);
|
||||||
|
|
||||||
Form.Designer = () => {
|
Form.Designer = () => {
|
||||||
const { name, title } = useCollection();
|
const { name, title } = useCollection();
|
||||||
|
@ -49,10 +49,13 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
|
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
|
||||||
|
@ -38,10 +38,13 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -38,10 +38,13 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -54,10 +54,13 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
|
<SchemaComponentProvider scope={{ useCloseAction }} components={{ Output, Form, Action, Input, FormItem }}>
|
||||||
|
@ -69,10 +69,13 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -39,10 +39,13 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -36,10 +36,13 @@ const schema: ISchema = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Output = observer(() => {
|
const Output = observer(
|
||||||
const form = useForm();
|
() => {
|
||||||
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
const form = useForm();
|
||||||
});
|
return <pre>{JSON.stringify(form.values, null, 2)}</pre>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Output' },
|
||||||
|
);
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -127,51 +127,54 @@ export const G2PlotRenderer = forwardRef(function <O = any>(props: ReactG2PlotPr
|
|||||||
return <div className={cls(['g2plot', className])} ref={containerRef} />;
|
return <div className={cls(['g2plot', className])} ref={containerRef} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const G2Plot: any = observer((props: any) => {
|
export const G2Plot: any = observer(
|
||||||
const { plot, config } = props;
|
(props: any) => {
|
||||||
const field = useField<Field>();
|
const { plot, config } = props;
|
||||||
const { t } = useTranslation();
|
const field = useField<Field>();
|
||||||
const api = useAPIClient();
|
const { t } = useTranslation();
|
||||||
useEffect(() => {
|
const api = useAPIClient();
|
||||||
field.data = field.data || {};
|
useEffect(() => {
|
||||||
field.data.loading = true;
|
field.data = field.data || {};
|
||||||
const fn = config?.data;
|
field.data.loading = true;
|
||||||
if (typeof fn === 'function') {
|
const fn = config?.data;
|
||||||
const result = fn.bind({ api })();
|
if (typeof fn === 'function') {
|
||||||
if (result?.then) {
|
const result = fn.bind({ api })();
|
||||||
result.then((data) => {
|
if (result?.then) {
|
||||||
if (Array.isArray(data)) {
|
result.then((data) => {
|
||||||
field.componentProps.config.data = data;
|
if (Array.isArray(data)) {
|
||||||
}
|
field.componentProps.config.data = data;
|
||||||
|
}
|
||||||
|
field.data.loading = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
field.data.loading = false;
|
field.data.loading = false;
|
||||||
});
|
}
|
||||||
} else {
|
} else {
|
||||||
field.data.loading = false;
|
field.data.loading = false;
|
||||||
}
|
}
|
||||||
} else {
|
}, []);
|
||||||
field.data.loading = false;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!plot || !config) {
|
if (!plot || !config) {
|
||||||
return <div style={{ opacity: 0.3 }}>{t('In configuration')}...</div>;
|
return <div style={{ opacity: 0.3 }}>{t('In configuration')}...</div>;
|
||||||
}
|
}
|
||||||
if (field?.data?.loading !== false) {
|
if (field?.data?.loading !== false) {
|
||||||
return <Spin />;
|
return <Spin />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{field.title && <h2>{field.title}</h2>}
|
{field.title && <h2>{field.title}</h2>}
|
||||||
<G2PlotRenderer
|
<G2PlotRenderer
|
||||||
plot={plots[plot]}
|
plot={plots[plot]}
|
||||||
config={{
|
config={{
|
||||||
...config,
|
...config,
|
||||||
data: Array.isArray(config?.data) ? config.data : [],
|
data: Array.isArray(config?.data) ? config.data : [],
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'G2Plot' },
|
||||||
|
);
|
||||||
|
|
||||||
G2Plot.Designer = G2PlotDesigner;
|
G2Plot.Designer = G2PlotDesigner;
|
||||||
G2Plot.plots = plots;
|
G2Plot.plots = plots;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { observer } from '@formily/react';
|
import { observer } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export const Event = observer((props) => {
|
export const Event = observer(
|
||||||
return <>{props.children}</>;
|
(props) => {
|
||||||
});
|
return <>{props.children}</>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Event' },
|
||||||
|
);
|
||||||
|
@ -2,12 +2,15 @@ import { observer, useFieldSchema } from '@formily/react';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DragHandler } from '../../';
|
import { DragHandler } from '../../';
|
||||||
|
|
||||||
export const Block = observer((props) => {
|
export const Block = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props) => {
|
||||||
return (
|
const fieldSchema = useFieldSchema();
|
||||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
return (
|
||||||
Block {fieldSchema.title}
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
<DragHandler />
|
Block {fieldSchema.title}
|
||||||
</div>
|
<DragHandler />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Block' },
|
||||||
|
);
|
||||||
|
@ -330,45 +330,108 @@ export const useGridRowContext = () => {
|
|||||||
return useContext(GridRowContext);
|
return useContext(GridRowContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Grid: any = observer((props: any) => {
|
export const Grid: any = observer(
|
||||||
const gridRef = useRef(null);
|
(props: any) => {
|
||||||
const field = useField();
|
const gridRef = useRef(null);
|
||||||
const fieldSchema = useFieldSchema();
|
const field = useField();
|
||||||
const { render, InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
const fieldSchema = useFieldSchema();
|
||||||
const addr = field.address.toString();
|
const { render, InitializerComponent } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||||
const rows = useRowProperties();
|
const addr = field.address.toString();
|
||||||
const { setPrintContent } = useFormBlockContext();
|
const rows = useRowProperties();
|
||||||
|
const { setPrintContent } = useFormBlockContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
gridRef.current && setPrintContent?.(gridRef.current);
|
gridRef.current && setPrintContent?.(gridRef.current);
|
||||||
}, [gridRef.current]);
|
}, [gridRef.current]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridContext.Provider value={{ ref: gridRef, fieldSchema, renderSchemaInitializer: render, InitializerComponent }}>
|
<GridContext.Provider
|
||||||
<div className={'nb-grid'} style={{ position: 'relative' }} ref={gridRef}>
|
value={{ ref: gridRef, fieldSchema, renderSchemaInitializer: render, InitializerComponent }}
|
||||||
<DndWrapper dndContext={props.dndContext}>
|
>
|
||||||
<RowDivider
|
<div className={'nb-grid'} style={{ position: 'relative' }} ref={gridRef}>
|
||||||
rows={rows}
|
<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
|
first
|
||||||
id={`${addr}_0`}
|
id={`${addr}_0`}
|
||||||
data={{
|
data={{
|
||||||
breakRemoveOn: breakRemoveOnGrid,
|
breakRemoveOn: breakRemoveOnRow,
|
||||||
wrapSchema: wrapRowSchema,
|
wrapSchema: wrapColSchema,
|
||||||
insertAdjacent: 'afterBegin',
|
insertAdjacent: 'afterBegin',
|
||||||
schema: fieldSchema,
|
schema: fieldSchema,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{rows.map((schema, index) => {
|
{cols.map((schema, index) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
<RecursionField name={schema.name} schema={schema} />
|
<RecursionField name={schema.name} schema={schema} />
|
||||||
<RowDivider
|
<ColDivider
|
||||||
rows={rows}
|
cols={cols}
|
||||||
index={index}
|
index={index}
|
||||||
|
last={index === cols.length - 1}
|
||||||
id={`${addr}_${index + 1}`}
|
id={`${addr}_${index + 1}`}
|
||||||
data={{
|
data={{
|
||||||
breakRemoveOn: breakRemoveOnGrid,
|
breakRemoveOn: breakRemoveOnRow,
|
||||||
wrapSchema: wrapRowSchema,
|
wrapSchema: wrapColSchema,
|
||||||
insertAdjacent: 'afterEnd',
|
insertAdjacent: 'afterEnd',
|
||||||
schema,
|
schema,
|
||||||
}}
|
}}
|
||||||
@ -376,95 +439,43 @@ export const Grid: any = observer((props: any) => {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</DndWrapper>
|
</div>
|
||||||
<InitializerComponent />
|
</GridRowContext.Provider>
|
||||||
</div>
|
);
|
||||||
</GridContext.Provider>
|
},
|
||||||
);
|
{ displayName: 'Grid.Row' },
|
||||||
});
|
);
|
||||||
|
|
||||||
Grid.Row = observer(() => {
|
Grid.Col = observer(
|
||||||
const field = useField();
|
(props: any) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const { cols = [] } = useContext(GridRowContext);
|
||||||
const addr = field.address.toString();
|
const schema = useFieldSchema();
|
||||||
const cols = useColProperties();
|
const field = useField();
|
||||||
|
|
||||||
return (
|
const width = useMemo(() => {
|
||||||
<GridRowContext.Provider value={{ schema: fieldSchema, cols }}>
|
let width = '';
|
||||||
<div
|
if (cols?.length) {
|
||||||
className={cls(
|
const w = schema?.['x-component-props']?.['width'] || 100 / cols.length;
|
||||||
'nb-grid-row',
|
width = `calc(${w}% - var(--nb-spacing) * ${(cols.length + 1) / cols.length})`;
|
||||||
css`
|
}
|
||||||
overflow-x: hidden;
|
return width;
|
||||||
margin: 0 calc(-1 * var(--nb-spacing));
|
}, [cols?.length, schema?.['x-component-props']?.['width']]);
|
||||||
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>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Grid.Col = observer((props: any) => {
|
const { setNodeRef } = useDroppable({
|
||||||
const { cols = [] } = useContext(GridRowContext);
|
id: field.address.toString(),
|
||||||
const schema = useFieldSchema();
|
data: {
|
||||||
const field = useField();
|
insertAdjacent: 'beforeEnd',
|
||||||
|
schema,
|
||||||
const width = useMemo(() => {
|
wrapSchema: (s) => s,
|
||||||
let width = '';
|
},
|
||||||
if (cols?.length) {
|
});
|
||||||
const w = schema?.['x-component-props']?.['width'] || 100 / cols.length;
|
return (
|
||||||
width = `calc(${w}% - var(--nb-spacing) * ${(cols.length + 1) / cols.length})`;
|
<GridColContext.Provider value={{ cols, schema }}>
|
||||||
}
|
<div ref={setNodeRef} style={{ width }} className={cls('nb-grid-col')}>
|
||||||
return width;
|
{props.children}
|
||||||
}, [cols?.length, schema?.['x-component-props']?.['width']]);
|
</div>
|
||||||
|
</GridColContext.Provider>
|
||||||
const { setNodeRef } = useDroppable({
|
);
|
||||||
id: field.address.toString(),
|
},
|
||||||
data: {
|
{ displayName: 'Grid.Col' },
|
||||||
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>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
@ -2,15 +2,18 @@ import { observer, useFieldSchema } from '@formily/react';
|
|||||||
import { BlockItem, DragHandler, Grid, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
import { BlockItem, DragHandler, Grid, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Block = observer((props) => {
|
const Block = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props) => {
|
||||||
return (
|
const fieldSchema = useFieldSchema();
|
||||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
return (
|
||||||
Block {fieldSchema.title}
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
<DragHandler />
|
Block {fieldSchema.title}
|
||||||
</div>
|
<DragHandler />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Block' },
|
||||||
|
);
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
_isJSONSchemaObject: true,
|
_isJSONSchemaObject: true,
|
||||||
|
@ -10,98 +10,101 @@ import { RecordProvider } from '../../../record-provider';
|
|||||||
import { SchemaComponentOptions } from '../../core/SchemaComponentOptions';
|
import { SchemaComponentOptions } from '../../core/SchemaComponentOptions';
|
||||||
import { KanbanCardContext } from './context';
|
import { KanbanCardContext } from './context';
|
||||||
|
|
||||||
export const KanbanCard: any = observer((props: any) => {
|
export const KanbanCard: any = observer(
|
||||||
const { setDisableCardDrag, cardViewerSchema, card, cardField, columnIndex, cardIndex } =
|
(props: any) => {
|
||||||
useContext(KanbanCardContext);
|
const { setDisableCardDrag, cardViewerSchema, card, cardField, columnIndex, cardIndex } =
|
||||||
const fieldSchema = useFieldSchema();
|
useContext(KanbanCardContext);
|
||||||
const [visible, setVisible] = useState(false);
|
const fieldSchema = useFieldSchema();
|
||||||
const labelDisabled = fieldSchema['x-label-disabled'];
|
const [visible, setVisible] = useState(false);
|
||||||
|
const labelDisabled = fieldSchema['x-label-disabled'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponentOptions components={{}} scope={{}}>
|
<SchemaComponentOptions components={{}} scope={{}}>
|
||||||
<Card
|
<Card
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}}
|
}}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
hoverable
|
hoverable
|
||||||
style={{ cursor: 'pointer', overflow: 'hidden' }}
|
style={{ cursor: 'pointer', overflow: 'hidden' }}
|
||||||
// bodyStyle={{ paddingBottom: 0 }}
|
// bodyStyle={{ paddingBottom: 0 }}
|
||||||
className={cls(
|
className={cls(
|
||||||
css`
|
css`
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
|
||||||
.nb-row-divider {
|
|
||||||
height: 16px;
|
|
||||||
margin-top: -16px;
|
|
||||||
&:last-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
}
|
||||||
}
|
.nb-row-divider {
|
||||||
.ant-description-input {
|
height: 16px;
|
||||||
text-overflow: ellipsis;
|
margin-top: -16px;
|
||||||
width: 100%;
|
&:last-child {
|
||||||
overflow: hidden;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.ant-description-textarea {
|
}
|
||||||
text-overflow: ellipsis;
|
.ant-description-input {
|
||||||
width: 100%;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
width: 100%;
|
||||||
}
|
overflow: hidden;
|
||||||
.ant-formily-item {
|
}
|
||||||
margin-bottom: 12px;
|
.ant-description-textarea {
|
||||||
}
|
text-overflow: ellipsis;
|
||||||
.nb-grid-row:last-of-type {
|
width: 100%;
|
||||||
.nb-grid-col {
|
overflow: hidden;
|
||||||
.nb-form-item:last-of-type {
|
}
|
||||||
.ant-formily-item {
|
.ant-formily-item {
|
||||||
margin-bottom: 0;
|
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>
|
||||||
)}
|
)}
|
||||||
>
|
</SchemaComponentOptions>
|
||||||
<DndContext
|
);
|
||||||
onDragStart={() => {
|
},
|
||||||
setDisableCardDrag(true);
|
{ displayName: 'KanbanCard' },
|
||||||
}}
|
);
|
||||||
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>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { observer } from '@formily/react';
|
import { observer } from '@formily/react';
|
||||||
|
|
||||||
export const KanbanCardViewer: any = observer((props: any) => {
|
export const KanbanCardViewer: any = observer(
|
||||||
return props.children;
|
(props: any) => {
|
||||||
});
|
return props.children;
|
||||||
|
},
|
||||||
|
{ displayName: 'KanbanCardViewer' },
|
||||||
|
);
|
||||||
|
@ -55,103 +55,106 @@ export const toColumns = (groupField: any, dataSource: Array<any> = []) => {
|
|||||||
return Object.values(columns);
|
return Object.values(columns);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Kanban: any = observer((props: any) => {
|
export const Kanban: any = observer(
|
||||||
const { groupField, onCardDragEnd, ...restProps } = useProps(props);
|
(props: any) => {
|
||||||
const field = useField<ArrayField>();
|
const { groupField, onCardDragEnd, ...restProps } = useProps(props);
|
||||||
const fieldSchema = useFieldSchema();
|
const field = useField<ArrayField>();
|
||||||
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
const fieldSchema = useFieldSchema();
|
||||||
const schemas = useMemo(
|
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
||||||
() =>
|
const schemas = useMemo(
|
||||||
fieldSchema.reduceProperties(
|
() =>
|
||||||
(buf, current) => {
|
fieldSchema.reduceProperties(
|
||||||
if (current['x-component'].endsWith('.Card')) {
|
(buf, current) => {
|
||||||
buf.card = current;
|
if (current['x-component'].endsWith('.Card')) {
|
||||||
} else if (current['x-component'].endsWith('.CardAdder')) {
|
buf.card = current;
|
||||||
buf.cardAdder = current;
|
} else if (current['x-component'].endsWith('.CardAdder')) {
|
||||||
} else if (current['x-component'].endsWith('.CardViewer')) {
|
buf.cardAdder = current;
|
||||||
buf.cardViewer = current;
|
} else if (current['x-component'].endsWith('.CardViewer')) {
|
||||||
}
|
buf.cardViewer = current;
|
||||||
return buf;
|
}
|
||||||
},
|
return buf;
|
||||||
{ card: null, cardAdder: null, cardViewer: null },
|
},
|
||||||
),
|
{ card: null, cardAdder: null, cardViewer: null },
|
||||||
[],
|
),
|
||||||
);
|
[],
|
||||||
const handleCardRemove = (card, column) => {
|
);
|
||||||
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
const handleCardRemove = (card, column) => {
|
||||||
field.value = updatedBoard.columns;
|
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 handleCardDragEnd = (card, fromColumn, toColumn) => {
|
||||||
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
||||||
field.value = updatedBoard.columns;
|
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
||||||
};
|
field.value = updatedBoard.columns;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Spin
|
<Spin
|
||||||
wrapperClassName={css`
|
wrapperClassName={css`
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
|
||||||
> .ant-spin-container {
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
> .ant-spin-container {
|
||||||
`}
|
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;
|
|
||||||
}
|
}
|
||||||
return (
|
`}
|
||||||
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
spinning={field.loading || false}
|
||||||
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
|
||||||
<RecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
|
||||||
</SchemaComponentOptions>
|
|
||||||
</KanbanColumnContext.Provider>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{{
|
<Board
|
||||||
columns: field.value || [],
|
{...restProps}
|
||||||
}}
|
allowAddCard={!!schemas.cardAdder}
|
||||||
</Board>
|
disableColumnDrag
|
||||||
</Spin>
|
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) => {
|
export const MarkdownVoid: any = observer(
|
||||||
const { content, className } = props;
|
(props: any) => {
|
||||||
const field = useField();
|
const { content, className } = props;
|
||||||
const schema = useFieldSchema();
|
const field = useField();
|
||||||
const { dn } = useDesignable();
|
const schema = useFieldSchema();
|
||||||
const { onSave, onCancel } = props;
|
const { dn } = useDesignable();
|
||||||
const { html, loading } = useParseMarkdown(content);
|
const { onSave, onCancel } = props;
|
||||||
if (loading) {
|
const { html, loading } = useParseMarkdown(content);
|
||||||
return <Spin />;
|
if (loading) {
|
||||||
}
|
return <Spin />;
|
||||||
return field?.editable ? (
|
}
|
||||||
<MarkdownEditor
|
return field?.editable ? (
|
||||||
{...props}
|
<MarkdownEditor
|
||||||
className
|
{...props}
|
||||||
defaultValue={content}
|
className
|
||||||
onCancel={() => {
|
defaultValue={content}
|
||||||
field.editable = false;
|
onCancel={() => {
|
||||||
onCancel?.();
|
field.editable = false;
|
||||||
}}
|
onCancel?.();
|
||||||
onSubmit={async (value) => {
|
}}
|
||||||
field.editable = false;
|
onSubmit={async (value) => {
|
||||||
schema['x-component-props'] ?? (schema['x-component-props'] = {});
|
field.editable = false;
|
||||||
schema['x-component-props']['content'] = value;
|
schema['x-component-props'] ?? (schema['x-component-props'] = {});
|
||||||
field.componentProps.content = value;
|
schema['x-component-props']['content'] = value;
|
||||||
onSave?.(schema);
|
field.componentProps.content = value;
|
||||||
dn.emit('patch', {
|
onSave?.(schema);
|
||||||
schema: {
|
dn.emit('patch', {
|
||||||
'x-uid': schema['x-uid'],
|
schema: {
|
||||||
'x-component-props': {
|
'x-uid': schema['x-uid'],
|
||||||
content: value,
|
'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;
|
MarkdownVoid.Designer = MarkdownVoidDesigner;
|
||||||
|
@ -23,24 +23,27 @@ const schema = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Editable = observer((props: any) => {
|
const Editable = observer(
|
||||||
const filed = useField<any>();
|
(props: any) => {
|
||||||
if (filed.editable) {
|
const filed = useField<any>();
|
||||||
return props.children;
|
if (filed.editable) {
|
||||||
}
|
return props.children;
|
||||||
return (
|
}
|
||||||
<div>
|
return (
|
||||||
<Button
|
<div>
|
||||||
onClick={() => {
|
<Button
|
||||||
filed.editable = true;
|
onClick={() => {
|
||||||
}}
|
filed.editable = true;
|
||||||
>
|
}}
|
||||||
编辑
|
>
|
||||||
</Button>
|
编辑
|
||||||
<div>{props.children}</div>
|
</Button>
|
||||||
</div>
|
<div>{props.children}</div>
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Editable' },
|
||||||
|
);
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
|
@ -136,148 +136,149 @@ const useSideMenuRef = () => {
|
|||||||
|
|
||||||
const MenuItemDesignerContext = createContext(null);
|
const MenuItemDesignerContext = createContext(null);
|
||||||
|
|
||||||
export const Menu: ComposedMenu = observer((props) => {
|
export const Menu: ComposedMenu = observer(
|
||||||
const {
|
(props) => {
|
||||||
onSelect,
|
const {
|
||||||
mode,
|
onSelect,
|
||||||
selectedUid,
|
mode,
|
||||||
defaultSelectedUid,
|
selectedUid,
|
||||||
sideMenuRefScopeKey,
|
defaultSelectedUid,
|
||||||
defaultSelectedKeys: dSelectedKeys,
|
sideMenuRefScopeKey,
|
||||||
defaultOpenKeys: dOpenKeys,
|
defaultSelectedKeys: dSelectedKeys,
|
||||||
...others
|
defaultOpenKeys: dOpenKeys,
|
||||||
} = useProps(props);
|
...others
|
||||||
const { t } = useTranslation();
|
} = useProps(props);
|
||||||
const Designer = useDesigner();
|
const { t } = useTranslation();
|
||||||
const schema = useFieldSchema();
|
const Designer = useDesigner();
|
||||||
const { refresh } = useDesignable();
|
const schema = useFieldSchema();
|
||||||
const api = useAPIClient();
|
const { refresh } = useDesignable();
|
||||||
const { render } = useSchemaInitializer(schema['x-initializer']);
|
const api = useAPIClient();
|
||||||
const sideMenuRef = useSideMenuRef();
|
const { render } = useSchemaInitializer(schema['x-initializer']);
|
||||||
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
const sideMenuRef = useSideMenuRef();
|
||||||
if (dSelectedKeys) {
|
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
||||||
return dSelectedKeys;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
if (defaultSelectedUid) {
|
||||||
return null;
|
return findKeysByUid(schema, defaultSelectedUid);
|
||||||
});
|
}
|
||||||
useEffect(() => {
|
return [];
|
||||||
const keys = findKeysByUid(schema, selectedUid);
|
});
|
||||||
setDefaultSelectedKeys(keys);
|
const [loading, setLoading] = useState(false);
|
||||||
if (['inline', 'mix'].includes(mode)) {
|
const [defaultOpenKeys, setDefaultOpenKeys] = useState(() => {
|
||||||
setDefaultOpenKeys(dOpenKeys || keys);
|
if (['inline', 'mix'].includes(mode)) {
|
||||||
}
|
return dOpenKeys || defaultSelectedKeys;
|
||||||
const key = keys?.[0] || null;
|
}
|
||||||
if (mode === 'mix') {
|
return dOpenKeys;
|
||||||
if (key) {
|
});
|
||||||
|
const [sideMenuSchema, setSideMenuSchema] = useState<Schema>(() => {
|
||||||
|
const key = defaultSelectedKeys?.[0] || null;
|
||||||
|
if (mode === 'mix' && key) {
|
||||||
const s = schema.properties?.[key];
|
const s = schema.properties?.[key];
|
||||||
if (s['x-component'] === 'Menu.SubMenu') {
|
if (s['x-component'] === 'Menu.SubMenu') {
|
||||||
setSideMenuSchema(s);
|
return s;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setSideMenuSchema(null);
|
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
}, [selectedUid]);
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (['inline', 'mix'].includes(mode)) {
|
const keys = findKeysByUid(schema, selectedUid);
|
||||||
setDefaultOpenKeys(defaultSelectedKeys);
|
setDefaultSelectedKeys(keys);
|
||||||
}
|
if (['inline', 'mix'].includes(mode)) {
|
||||||
}, [defaultSelectedKeys]);
|
setDefaultOpenKeys(dOpenKeys || keys);
|
||||||
useEffect(() => {
|
}
|
||||||
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
const key = keys?.[0] || null;
|
||||||
if (!sideMenuElement) {
|
if (mode === 'mix') {
|
||||||
return;
|
if (key) {
|
||||||
}
|
const s = schema.properties?.[key];
|
||||||
sideMenuElement.style.display = sideMenuSchema?.['x-component'] === 'Menu.SubMenu' ? 'block' : 'none';
|
if (s['x-component'] === 'Menu.SubMenu') {
|
||||||
}, [sideMenuSchema?.name, sideMenuRef]);
|
setSideMenuSchema(s);
|
||||||
const { designable } = useDesignable();
|
}
|
||||||
return (
|
} else {
|
||||||
<DndContext>
|
setSideMenuSchema(null);
|
||||||
<MenuItemDesignerContext.Provider value={Designer}>
|
}
|
||||||
<MenuModeContext.Provider value={mode}>
|
}
|
||||||
<AntdMenu
|
}, [selectedUid]);
|
||||||
{...others}
|
useEffect(() => {
|
||||||
style={
|
if (['inline', 'mix'].includes(mode)) {
|
||||||
{
|
setDefaultOpenKeys(defaultSelectedKeys);
|
||||||
// width: mode === 'mix' ? '100%' : undefined,
|
}
|
||||||
}
|
}, [defaultSelectedKeys]);
|
||||||
}
|
useEffect(() => {
|
||||||
className={css`
|
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
||||||
.ant-menu-item:hover {
|
if (!sideMenuElement) {
|
||||||
> .ant-menu-title-content > div {
|
return;
|
||||||
.general-schema-designer {
|
}
|
||||||
display: block;
|
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`
|
||||||
onSelect={(info: any) => {
|
.ant-menu-item:hover {
|
||||||
const s = schema.properties[info.key];
|
> .ant-menu-title-content > div {
|
||||||
if (mode === 'mix') {
|
.general-schema-designer {
|
||||||
setSideMenuSchema(s);
|
display: block;
|
||||||
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']);
|
onSelect={(info: any) => {
|
||||||
setDefaultSelectedKeys(keys);
|
const s = schema.properties[info.key];
|
||||||
setTimeout(() => {
|
if (mode === 'mix') {
|
||||||
setLoading(false);
|
setSideMenuSchema(s);
|
||||||
}, 100);
|
if (s['x-component'] !== 'Menu.SubMenu') {
|
||||||
onSelect &&
|
onSelect && onSelect(info);
|
||||||
onSelect({
|
} else {
|
||||||
key: menuItemSchema.name,
|
const menuItemSchema = findMenuItem(s);
|
||||||
item: {
|
if (!menuItemSchema) {
|
||||||
props: {
|
return;
|
||||||
schema: menuItemSchema,
|
}
|
||||||
|
// 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}
|
||||||
mode={mode === 'mix' ? 'horizontal' : mode}
|
selectedKeys={defaultSelectedKeys}
|
||||||
defaultOpenKeys={defaultOpenKeys}
|
>
|
||||||
defaultSelectedKeys={defaultSelectedKeys}
|
{designable && (
|
||||||
selectedKeys={defaultSelectedKeys}
|
<AntdMenu.Item disabled key="x-designer-button" style={{ padding: '0 8px', order: 9999 }}>
|
||||||
>
|
{render({ style: { background: 'none' } })}
|
||||||
{designable && (
|
</AntdMenu.Item>
|
||||||
<AntdMenu.Item disabled key="x-designer-button" style={{ padding: '0 8px', order: 9999 }}>
|
)}
|
||||||
{render({ style: { background: 'none' } })}
|
{props.children}
|
||||||
</AntdMenu.Item>
|
</AntdMenu>
|
||||||
)}
|
{loading
|
||||||
{props.children}
|
? null
|
||||||
</AntdMenu>
|
: mode === 'mix' &&
|
||||||
{loading
|
|
||||||
? null
|
|
||||||
: mode === 'mix' &&
|
|
||||||
sideMenuSchema?.['x-component'] === 'Menu.SubMenu' &&
|
sideMenuSchema?.['x-component'] === 'Menu.SubMenu' &&
|
||||||
sideMenuRef?.current?.firstChild &&
|
sideMenuRef?.current?.firstChild &&
|
||||||
createPortal(
|
createPortal(
|
||||||
@ -290,37 +291,37 @@ export const Menu: ComposedMenu = observer((props) => {
|
|||||||
onSelect && onSelect(info);
|
onSelect && onSelect(info);
|
||||||
}}
|
}}
|
||||||
className={css`
|
className={css`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
.ant-menu-item {
|
.ant-menu-item {
|
||||||
> .ant-menu-title-content {
|
> .ant-menu-title-content {
|
||||||
margin-left: -24px;
|
margin-left: -24px;
|
||||||
margin-right: -16px;
|
margin-right: -16px;
|
||||||
padding: 0 16px 0 24px;
|
padding: 0 16px 0 24px;
|
||||||
> div {
|
> div {
|
||||||
> .general-schema-designer {
|
> .general-schema-designer {
|
||||||
right: 6px !important;
|
right: 6px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.ant-menu-submenu-title {
|
||||||
.ant-menu-submenu-title {
|
.ant-menu-title-content {
|
||||||
.ant-menu-title-content {
|
margin-left: -24px;
|
||||||
margin-left: -24px;
|
margin-right: -34px;
|
||||||
margin-right: -34px;
|
padding: 0 34px 0 24px;
|
||||||
padding: 0 34px 0 24px;
|
> div {
|
||||||
> div {
|
> .general-schema-designer {
|
||||||
> .general-schema-designer {
|
right: 6px !important;
|
||||||
right: 6px !important;
|
}
|
||||||
}
|
> span.anticon {
|
||||||
> span.anticon {
|
margin-right: 10px;
|
||||||
margin-right: 10px;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
`}
|
||||||
`}
|
|
||||||
>
|
>
|
||||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
||||||
{render({
|
{render({
|
||||||
@ -340,117 +341,128 @@ export const Menu: ComposedMenu = observer((props) => {
|
|||||||
</MenuModeContext.Provider>,
|
</MenuModeContext.Provider>,
|
||||||
sideMenuRef.current.firstChild,
|
sideMenuRef.current.firstChild,
|
||||||
)}
|
)}
|
||||||
</MenuModeContext.Provider>
|
</MenuModeContext.Provider>
|
||||||
</MenuItemDesignerContext.Provider>
|
</MenuItemDesignerContext.Provider>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'Menu' },
|
||||||
|
);
|
||||||
|
|
||||||
Menu.Item = observer((props) => {
|
Menu.Item = observer(
|
||||||
const { icon, ...others } = props;
|
(props) => {
|
||||||
const schema = useFieldSchema();
|
const { icon, ...others } = props;
|
||||||
const field = useField();
|
const schema = useFieldSchema();
|
||||||
const Designer = useContext(MenuItemDesignerContext);
|
const field = useField();
|
||||||
return (
|
const Designer = useContext(MenuItemDesignerContext);
|
||||||
<AntdMenu.Item
|
return (
|
||||||
{...others}
|
<AntdMenu.Item
|
||||||
className={css`
|
{...others}
|
||||||
:active {
|
className={css`
|
||||||
background: inherit;
|
:active {
|
||||||
}
|
background: inherit;
|
||||||
`}
|
}
|
||||||
key={schema.name}
|
`}
|
||||||
eventKey={schema.name}
|
key={schema.name}
|
||||||
schema={schema}
|
eventKey={schema.name}
|
||||||
>
|
schema={schema}
|
||||||
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
>
|
||||||
<Icon type={icon} />
|
<SortableItem className={designerCss} removeParentsIfNoChildren={false}>
|
||||||
<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}>
|
|
||||||
<Icon type={icon} />
|
<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 />
|
<Designer />
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
}
|
</AntdMenu.Item>
|
||||||
>
|
);
|
||||||
<RecursionField schema={schema} onlyRenderProperties />
|
},
|
||||||
</AntdMenu.SubMenu>
|
{ 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;
|
Menu.Designer = MenuDesigner;
|
||||||
|
@ -3,20 +3,23 @@ import { Pagination as AntdPagination } from 'antd';
|
|||||||
import React, { KeyboardEventHandler } from 'react';
|
import React, { KeyboardEventHandler } from 'react';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
|
|
||||||
export const Pagination = observer((props: any) => {
|
export const Pagination = observer(
|
||||||
const { hidden, ...others } = useProps(props);
|
(props: any) => {
|
||||||
if (hidden) {
|
const { hidden, ...others } = useProps(props);
|
||||||
return null;
|
if (hidden) {
|
||||||
}
|
return null;
|
||||||
const onKeypress: KeyboardEventHandler<HTMLDivElement> = (e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
}
|
||||||
};
|
const onKeypress: KeyboardEventHandler<HTMLDivElement> = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onKeyPress={onKeypress}>
|
<div onKeyPress={onKeypress}>
|
||||||
<AntdPagination {...others} />
|
<AntdPagination {...others} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'Pagination' },
|
||||||
|
);
|
||||||
|
@ -25,98 +25,101 @@ const toValue = (value, placeholder) => {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReadPrettyRecordPicker: React.FC = observer((props: any) => {
|
export const ReadPrettyRecordPicker: React.FC = observer(
|
||||||
const { ellipsis } = props;
|
(props: any) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const { ellipsis } = props;
|
||||||
const recordCtx = useRecord();
|
const fieldSchema = useFieldSchema();
|
||||||
const { getCollectionJoinField } = useCollectionManager();
|
const recordCtx = useRecord();
|
||||||
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
const { getCollectionJoinField } = useCollectionManager();
|
||||||
// const field = useField<Field>();
|
// value 做了转换,但 props.value 和原来 useField().value 的值不一致
|
||||||
const fieldNames = useFieldNames(props);
|
// const field = useField<Field>();
|
||||||
const [visible, setVisible] = useState(false);
|
const fieldNames = useFieldNames(props);
|
||||||
const { getField } = useCollection();
|
const [visible, setVisible] = useState(false);
|
||||||
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
const { getField } = useCollection();
|
||||||
const [record, setRecord] = useState({});
|
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||||
const compile = useCompile();
|
const [record, setRecord] = useState({});
|
||||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
const compile = useCompile();
|
||||||
const showFilePicker = isShowFilePicker(labelUiSchema);
|
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||||
const { snapshot } = useActionContext();
|
const showFilePicker = isShowFilePicker(labelUiSchema);
|
||||||
const isTagsMode = fieldSchema['x-component-props']?.mode === 'tags';
|
const { snapshot } = useActionContext();
|
||||||
|
const isTagsMode = fieldSchema['x-component-props']?.mode === 'tags';
|
||||||
|
|
||||||
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
const ellipsisWithTooltipRef = useRef<IEllipsisWithTooltipRef>();
|
||||||
|
|
||||||
if (showFilePicker) {
|
if (showFilePicker) {
|
||||||
return collectionField ? <Preview {...props} /> : null;
|
return collectionField ? <Preview {...props} /> : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderRecords = () =>
|
const renderRecords = () =>
|
||||||
toArr(props.value).map((record, index, arr) => {
|
toArr(props.value).map((record, index, arr) => {
|
||||||
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
const val = toValue(compile(record?.[fieldNames?.label || 'label']), 'N/A');
|
||||||
const text = getLabelFormatValue(labelUiSchema, val, true);
|
const text = getLabelFormatValue(labelUiSchema, val, true);
|
||||||
return (
|
return (
|
||||||
<Fragment key={`${record.id}_${index}`}>
|
<Fragment key={`${record.id}_${index}`}>
|
||||||
<span>
|
<span>
|
||||||
{snapshot || isTagsMode ? (
|
{snapshot || isTagsMode ? (
|
||||||
text
|
text
|
||||||
) : (
|
) : (
|
||||||
<a
|
<a
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
setRecord(record);
|
setRecord(record);
|
||||||
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
ellipsisWithTooltipRef?.current?.setPopoverVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
{index < arr.length - 1 ? <span style={{ marginRight: 4, color: '#aaa' }}>,</span> : null}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderWithoutTableFieldResourceProvider = () => (
|
const renderWithoutTableFieldResourceProvider = () => (
|
||||||
<WithoutTableFieldResource.Provider value={true}>
|
<WithoutTableFieldResource.Provider value={true}>
|
||||||
<FormProvider>
|
<FormProvider>
|
||||||
<RecursionField
|
<RecursionField
|
||||||
schema={fieldSchema}
|
schema={fieldSchema}
|
||||||
onlyRenderProperties
|
onlyRenderProperties
|
||||||
filterProperties={(s) => {
|
filterProperties={(s) => {
|
||||||
return s['x-component'] === 'RecordPicker.Viewer';
|
return s['x-component'] === 'RecordPicker.Viewer';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</WithoutTableFieldResource.Provider>
|
</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>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
return collectionField ? (
|
const renderRecordProvider = () => {
|
||||||
<div>
|
const collectionFieldNames = fieldSchema?.['x-collection-field']?.split('.');
|
||||||
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
|
|
||||||
<CollectionProvider name={collectionField.target ?? collectionField.targetCollection}>
|
return collectionFieldNames && collectionFieldNames.length > 2 ? (
|
||||||
<EllipsisWithTooltip ellipsis={ellipsis} ref={ellipsisWithTooltipRef}>
|
<RecordProvider record={recordCtx[collectionFieldNames[1]]}>
|
||||||
{renderRecords()}
|
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||||
</EllipsisWithTooltip>
|
</RecordProvider>
|
||||||
<ActionContext.Provider
|
) : (
|
||||||
value={{ visible, setVisible, openMode: 'drawer', snapshot: collectionField.interface === 'snapshot' }}
|
<RecordProvider record={record}>{renderWithoutTableFieldResourceProvider()}</RecordProvider>
|
||||||
>
|
);
|
||||||
{renderRecordProvider()}
|
};
|
||||||
</ActionContext.Provider>
|
|
||||||
</CollectionProvider>
|
return collectionField ? (
|
||||||
</BlockAssociationContext.Provider>
|
<div>
|
||||||
</div>
|
<BlockAssociationContext.Provider value={`${collectionField.collectionName}.${collectionField.name}`}>
|
||||||
) : null;
|
<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 { useRecord } from '../../../record-provider';
|
||||||
import { useActionContext } from '../action';
|
import { useActionContext } from '../action';
|
||||||
|
|
||||||
export const ReadPretty = observer((props: any) => {
|
export const ReadPretty = observer(
|
||||||
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
(props: any) => {
|
||||||
const field = useField<any>();
|
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
||||||
const fieldSchema = useFieldSchema();
|
const field = useField<any>();
|
||||||
const record = useRecord();
|
const fieldSchema = useFieldSchema();
|
||||||
const { snapshot } = useActionContext();
|
const record = useRecord();
|
||||||
|
const { snapshot } = useActionContext();
|
||||||
|
|
||||||
const { data } = useRequest(
|
const { data } = useRequest(
|
||||||
snapshot
|
snapshot
|
||||||
? async () => ({
|
? async () => ({
|
||||||
data: record[fieldSchema.name],
|
data: record[fieldSchema.name],
|
||||||
})
|
})
|
||||||
: {
|
: {
|
||||||
action: 'list',
|
action: 'list',
|
||||||
...props.service,
|
...props.service,
|
||||||
params: {
|
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>;
|
Object?: React.FC<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReadPretty = observer((props: any) => {
|
export const ReadPretty = observer(
|
||||||
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
(props: any) => {
|
||||||
const field = useField<any>();
|
const fieldNames = { ...defaultFieldNames, ...props.fieldNames };
|
||||||
const compile = useCompile();
|
const field = useField<any>();
|
||||||
|
const compile = useCompile();
|
||||||
|
|
||||||
if (!isValid(props.value)) {
|
if (!isValid(props.value)) {
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
if (isArrayField(field) && field?.value?.length === 0) {
|
if (isArrayField(field) && field?.value?.length === 0) {
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
const dataSource = field.dataSource || props.options || [];
|
const dataSource = field.dataSource || props.options || [];
|
||||||
const options = getCurrentOptions(field.value, dataSource, fieldNames);
|
const options = getCurrentOptions(field.value, dataSource, fieldNames);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<EllipsisWithTooltip ellipsis={props.ellipsis}>
|
<EllipsisWithTooltip ellipsis={props.ellipsis}>
|
||||||
{options.map((option, key) => (
|
{options.map((option, key) => (
|
||||||
<Tag key={key} color={option[fieldNames.color]} icon={option.icon}>
|
<Tag key={key} color={option[fieldNames.color]} icon={option.icon}>
|
||||||
{compile(option[fieldNames.label])}
|
{compile(option[fieldNames.label])}
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</EllipsisWithTooltip>
|
</EllipsisWithTooltip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
{ displayName: 'ReadPretty' },
|
||||||
|
);
|
||||||
|
@ -42,12 +42,15 @@ export const designerCss = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TableColumnActionBar = observer((props) => {
|
export const TableColumnActionBar = observer(
|
||||||
const Designer = useDesigner();
|
(props) => {
|
||||||
return (
|
const Designer = useDesigner();
|
||||||
<SortableItem className={designerCss}>
|
return (
|
||||||
<Designer />
|
<SortableItem className={designerCss}>
|
||||||
{props.children}
|
<Designer />
|
||||||
</SortableItem>
|
{props.children}
|
||||||
);
|
</SortableItem>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'TableColumnActionBar' },
|
||||||
|
);
|
||||||
|
@ -163,164 +163,165 @@ const useValidator = (validator: (value: any) => string) => {
|
|||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Table: any = observer((props: any) => {
|
export const Table: any = observer(
|
||||||
const { pagination: pagination1, useProps, onChange, ...others1 } = props;
|
(props: any) => {
|
||||||
const { pagination: pagination2, onClickRow, ...others2 } = useProps?.() || {};
|
const { pagination: pagination1, useProps, onChange, ...others1 } = props;
|
||||||
const {
|
const { pagination: pagination2, onClickRow, ...others2 } = useProps?.() || {};
|
||||||
dragSort = false,
|
const {
|
||||||
showIndex = true,
|
dragSort = false,
|
||||||
onRowSelectionChange,
|
showIndex = true,
|
||||||
onChange: onTableChange,
|
onRowSelectionChange,
|
||||||
rowSelection,
|
onChange: onTableChange,
|
||||||
rowKey,
|
rowSelection,
|
||||||
required,
|
rowKey,
|
||||||
onExpand,
|
required,
|
||||||
...others
|
onExpand,
|
||||||
} = { ...others1, ...others2 } as any;
|
...others
|
||||||
const field = useArrayField(others);
|
} = { ...others1, ...others2 } as any;
|
||||||
const columns = useTableColumns(others);
|
const field = useArrayField(others);
|
||||||
const schema = useFieldSchema();
|
const columns = useTableColumns(others);
|
||||||
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
const schema = useFieldSchema();
|
||||||
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
||||||
const { expandFlag } = ctx;
|
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
||||||
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => {}));
|
const { expandFlag } = ctx;
|
||||||
const paginationProps = usePaginationProps(pagination1, pagination2);
|
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => { }));
|
||||||
const requiredValidator = field.required || required;
|
const paginationProps = usePaginationProps(pagination1, pagination2);
|
||||||
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
const requiredValidator = field.required || required;
|
||||||
const [expandedKeys, setExpandesKeys] = useState([]);
|
const { treeTable } = schema?.parent?.['x-decorator-props'] || {};
|
||||||
const [allIncludesChildren, setAllIncludesChildren] = useState([]);
|
const [expandedKeys, setExpandesKeys] = useState([]);
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>(field?.data?.selectedRowKeys || []);
|
const [allIncludesChildren, setAllIncludesChildren] = useState([]);
|
||||||
const [selectedRow, setSelectedRow] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>(field?.data?.selectedRowKeys || []);
|
||||||
|
const [selectedRow, setSelectedRow] = useState([]);
|
||||||
|
|
||||||
let onRow = null,
|
let onRow = null,
|
||||||
highlightRow = '';
|
highlightRow = '';
|
||||||
|
|
||||||
if (onClickRow) {
|
if (onClickRow) {
|
||||||
onRow = (record) => {
|
onRow = (record) => {
|
||||||
return {
|
return {
|
||||||
onClick: () => onClickRow(record, setSelectedRow, selectedRow),
|
onClick: () => onClickRow(record, setSelectedRow, selectedRow),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
highlightRow = css`
|
||||||
highlightRow = css`
|
& > td {
|
||||||
& > td {
|
background-color: #caedff !important;
|
||||||
background-color: #caedff !important;
|
}
|
||||||
}
|
&:hover > td {
|
||||||
&:hover > td {
|
background-color: #caedff !important;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}, [field.value]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (expandFlag) {
|
|
||||||
setExpandesKeys(allIncludesChildren);
|
|
||||||
} else {
|
|
||||||
setExpandesKeys([]);
|
|
||||||
}
|
|
||||||
}, [expandFlag, allIncludesChildren]);
|
|
||||||
|
|
||||||
const components = useMemo(() => {
|
useEffect(() => {
|
||||||
return {
|
field.setValidator((value) => {
|
||||||
header: {
|
if (requiredValidator) {
|
||||||
wrapper: (props) => {
|
return Array.isArray(value) && value.length > 0 ? null : 'The field value is required';
|
||||||
return (
|
}
|
||||||
<DndContext>
|
return;
|
||||||
<thead {...props} />
|
});
|
||||||
</DndContext>
|
}, [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) => {
|
body: {
|
||||||
return (
|
wrapper: (props) => {
|
||||||
<th
|
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}
|
{...props}
|
||||||
className={cls(
|
className={classNames(
|
||||||
props.className,
|
props.className,
|
||||||
css`
|
css`
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
&:hover .general-schema-designer {
|
.nb-read-pretty-input-number {
|
||||||
display: block;
|
text-align: right;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
),
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
body: {
|
}, [field, onRowDragEnd, dragSort]);
|
||||||
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 defaultRowKey = (record: any) => {
|
||||||
const toIndex = e.over?.data.current?.sortable?.index;
|
return field.value?.indexOf?.(record);
|
||||||
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;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}, [field, onRowDragEnd, dragSort]);
|
|
||||||
|
|
||||||
const defaultRowKey = (record: any) => {
|
const getRowKey = (record: any) => {
|
||||||
return field.value?.indexOf?.(record);
|
if (typeof rowKey === 'string') {
|
||||||
};
|
return record[rowKey]?.toString();
|
||||||
|
} else {
|
||||||
|
return (rowKey ?? defaultRowKey)(record)?.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getRowKey = (record: any) => {
|
const restProps = {
|
||||||
if (typeof rowKey === 'string') {
|
rowSelection: rowSelection
|
||||||
return record[rowKey]?.toString();
|
? {
|
||||||
} else {
|
|
||||||
return (rowKey ?? defaultRowKey)(record)?.toString();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const restProps = {
|
|
||||||
rowSelection: rowSelection
|
|
||||||
? {
|
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
selectedRowKeys: selectedRowKeys,
|
selectedRowKeys: selectedRowKeys,
|
||||||
onChange(selectedRowKeys: any[], selectedRows: any[]) {
|
onChange(selectedRowKeys: any[], selectedRows: any[]) {
|
||||||
@ -348,40 +349,40 @@ export const Table: any = observer((props: any) => {
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
float: left;
|
float: left;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
.nb-table-index {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
&:not(.checked) {
|
|
||||||
.nb-table-index {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.nb-table-index {
|
.nb-table-index {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.nb-origin-node {
|
&:not(.checked) {
|
||||||
display: block;
|
.nb-table-index {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
&:hover {
|
||||||
`,
|
.nb-table-index {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.nb-origin-node {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{dragSort && <SortHandle id={getRowKey(record)} />}
|
{dragSort && <SortHandle id={getRowKey(record)} />}
|
||||||
@ -392,13 +393,13 @@ export const Table: any = observer((props: any) => {
|
|||||||
'nb-origin-node',
|
'nb-origin-node',
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 50%;
|
right: 50%;
|
||||||
transform: translateX(50%);
|
transform: translateX(50%);
|
||||||
&:not(.checked) {
|
&:not(.checked) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{originNode}
|
{originNode}
|
||||||
@ -408,94 +409,96 @@ export const Table: any = observer((props: any) => {
|
|||||||
},
|
},
|
||||||
...rowSelection,
|
...rowSelection,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SortableWrapper = useCallback<React.FC>(
|
const SortableWrapper = useCallback<React.FC>(
|
||||||
({ children }) => {
|
({ children }) => {
|
||||||
return dragSort
|
return dragSort
|
||||||
? React.createElement(SortableContext, {
|
? React.createElement(SortableContext, {
|
||||||
items: field.value.map(getRowKey),
|
items: field.value.map(getRowKey),
|
||||||
children: children,
|
children: children,
|
||||||
})
|
})
|
||||||
: React.createElement(React.Fragment, {
|
: React.createElement(React.Fragment, {
|
||||||
children,
|
children,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[field, dragSort],
|
[field, dragSort],
|
||||||
);
|
);
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const fixedBlock = fieldSchema?.parent?.['x-decorator-props']?.fixedBlock;
|
const fixedBlock = fieldSchema?.parent?.['x-decorator-props']?.fixedBlock;
|
||||||
|
|
||||||
const { height: tableHeight, tableSizeRefCallback } = useTableSize();
|
const { height: tableHeight, tableSizeRefCallback } = useTableSize();
|
||||||
|
|
||||||
const scroll = useMemo(() => {
|
const scroll = useMemo(() => {
|
||||||
return fixedBlock
|
return fixedBlock
|
||||||
? {
|
? {
|
||||||
x: 'max-content',
|
x: 'max-content',
|
||||||
y: tableHeight,
|
y: tableHeight,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
x: 'max-content',
|
x: 'max-content',
|
||||||
};
|
};
|
||||||
}, [fixedBlock, tableHeight]);
|
}, [fixedBlock, tableHeight]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css`
|
className={css`
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
.ant-table-wrapper {
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.ant-spin-nested-loading {
|
overflow: hidden;
|
||||||
|
.ant-table-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.ant-spin-container {
|
.ant-spin-nested-loading {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
.ant-spin-container {
|
||||||
flex-direction: column;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.ant-table {
|
||||||
.ant-table {
|
overflow-x: auto;
|
||||||
overflow-x: auto;
|
overflow-y: hidden;
|
||||||
overflow-y: hidden;
|
}
|
||||||
}
|
`}
|
||||||
`}
|
>
|
||||||
>
|
<SortableWrapper>
|
||||||
<SortableWrapper>
|
<AntdTable
|
||||||
<AntdTable
|
ref={tableSizeRefCallback}
|
||||||
ref={tableSizeRefCallback}
|
rowKey={rowKey ?? defaultRowKey}
|
||||||
rowKey={rowKey ?? defaultRowKey}
|
dataSource={field?.value?.slice?.()}
|
||||||
dataSource={field?.value?.slice?.()}
|
{...others}
|
||||||
{...others}
|
{...restProps}
|
||||||
{...restProps}
|
pagination={paginationProps}
|
||||||
pagination={paginationProps}
|
components={components}
|
||||||
components={components}
|
onChange={(pagination, filters, sorter, extra) => {
|
||||||
onChange={(pagination, filters, sorter, extra) => {
|
onTableChange?.(pagination, filters, sorter, extra);
|
||||||
onTableChange?.(pagination, filters, sorter, extra);
|
}}
|
||||||
}}
|
onRow={onRow}
|
||||||
onRow={onRow}
|
rowClassName={(record) => (selectedRow.includes(record[rowKey]) ? highlightRow : '')}
|
||||||
rowClassName={(record) => (selectedRow.includes(record[rowKey]) ? highlightRow : '')}
|
tableLayout={'auto'}
|
||||||
tableLayout={'auto'}
|
scroll={scroll}
|
||||||
scroll={scroll}
|
columns={columns}
|
||||||
columns={columns}
|
expandable={{
|
||||||
expandable={{
|
onExpand: (flag, record) => {
|
||||||
onExpand: (flag, record) => {
|
const newKeys = flag ? [...expandedKeys, record.id] : expandedKeys.filter((i) => record.id !== i);
|
||||||
const newKeys = flag ? [...expandedKeys, record.id] : expandedKeys.filter((i) => record.id !== i);
|
setExpandesKeys(newKeys);
|
||||||
setExpandesKeys(newKeys);
|
onExpand?.(flag, record);
|
||||||
onExpand?.(flag, record);
|
},
|
||||||
},
|
expandedRowKeys: expandedKeys,
|
||||||
expandedRowKeys: expandedKeys,
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</SortableWrapper>
|
||||||
</SortableWrapper>
|
{field.errors.length > 0 && (
|
||||||
{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">
|
||||||
<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) => {
|
||||||
{field.errors.map((error) => {
|
return error.messages.map((message) => <div>{message}</div>);
|
||||||
return error.messages.map((message) => <div>{message}</div>);
|
})}
|
||||||
})}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
},
|
||||||
});
|
{ displayName: 'Table' },
|
||||||
|
);
|
||||||
|
@ -6,36 +6,42 @@ import { useCollection } from '../../../collection-manager';
|
|||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
import { ActionBar } from '../action';
|
import { ActionBar } from '../action';
|
||||||
|
|
||||||
export const TableField: any = observer((props) => {
|
export const TableField: any = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props) => {
|
||||||
const { getField } = useCollection();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField<Field>();
|
const { getField } = useCollection();
|
||||||
const collectionField = getField(fieldSchema.name);
|
const field = useField<Field>();
|
||||||
const compile = useCompile();
|
const collectionField = getField(fieldSchema.name);
|
||||||
const ctx = useFormBlockContext();
|
const compile = useCompile();
|
||||||
useEffect(() => {
|
const ctx = useFormBlockContext();
|
||||||
if (!field.title) {
|
useEffect(() => {
|
||||||
field.title = compile(collectionField?.uiSchema?.title);
|
if (!field.title) {
|
||||||
}
|
field.title = compile(collectionField?.uiSchema?.title);
|
||||||
if (ctx?.field) {
|
}
|
||||||
ctx.field.added = ctx.field.added || new Set();
|
if (ctx?.field) {
|
||||||
ctx.field.added.add(fieldSchema.name);
|
ctx.field.added = ctx.field.added || new Set();
|
||||||
}
|
ctx.field.added.add(fieldSchema.name);
|
||||||
}, []);
|
}
|
||||||
useEffect(() => {
|
}, []);
|
||||||
field.decoratorProps.asterisk = fieldSchema.required;
|
useEffect(() => {
|
||||||
}, [fieldSchema.required]);
|
field.decoratorProps.asterisk = fieldSchema.required;
|
||||||
return <div>{props.children}</div>;
|
}, [fieldSchema.required]);
|
||||||
});
|
return <div>{props.children}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'TableField' },
|
||||||
|
);
|
||||||
|
|
||||||
TableField.ActionBar = observer((props) => {
|
TableField.ActionBar = observer(
|
||||||
const form = useForm();
|
(props) => {
|
||||||
if (form.readPretty) {
|
const form = useForm();
|
||||||
return null;
|
if (form.readPretty) {
|
||||||
}
|
return null;
|
||||||
return (
|
}
|
||||||
<div style={{ marginBottom: 8, marginTop: -32 }}>
|
return (
|
||||||
<ActionBar {...props} />
|
<div style={{ marginBottom: 8, marginTop: -32 }}>
|
||||||
</div>
|
<ActionBar {...props} />
|
||||||
);
|
</div>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'TableField.ActionBar' },
|
||||||
|
);
|
||||||
|
@ -4,27 +4,30 @@ import { SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
|||||||
import { Table, TableColumnType } from 'antd';
|
import { Table, TableColumnType } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const ArrayTable = observer((props: any) => {
|
const ArrayTable = observer(
|
||||||
const { rowKey } = props;
|
(props: any) => {
|
||||||
const field = useField<ArrayField>();
|
const { rowKey } = props;
|
||||||
const schema = useFieldSchema();
|
const field = useField<ArrayField>();
|
||||||
const columnSchemas = schema.reduceProperties((buf, s) => {
|
const schema = useFieldSchema();
|
||||||
if (s['x-component'] === 'ArrayTable.Column') {
|
const columnSchemas = schema.reduceProperties((buf, s) => {
|
||||||
buf.push(s);
|
if (s['x-component'] === 'ArrayTable.Column') {
|
||||||
}
|
buf.push(s);
|
||||||
return buf;
|
}
|
||||||
}, []);
|
return buf;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const columns = columnSchemas.map((s) => {
|
const columns = columnSchemas.map((s) => {
|
||||||
return {
|
return {
|
||||||
render: (value, record) => {
|
render: (value, record) => {
|
||||||
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
return <RecursionField name={record.__path} schema={s} onlyRenderProperties />;
|
||||||
},
|
},
|
||||||
} as TableColumnType<any>;
|
} 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) => {
|
const Value = connect((props) => {
|
||||||
return <li>value: {props.value}</li>;
|
return <li>value: {props.value}</li>;
|
||||||
|
@ -151,28 +151,29 @@ const useDefAction = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TableArray: React.FC<any> = observer((props) => {
|
export const TableArray: React.FC<any> = observer(
|
||||||
const field = useField<ArrayField>();
|
(props) => {
|
||||||
const columns = useTableColumns();
|
const field = useField<ArrayField>();
|
||||||
const {
|
const columns = useTableColumns();
|
||||||
dragSort = false,
|
const {
|
||||||
showIndex = true,
|
dragSort = false,
|
||||||
useSelectedRowKeys = useDef,
|
showIndex = true,
|
||||||
useDataSource = useDefDataSource,
|
useSelectedRowKeys = useDef,
|
||||||
useAction = useDefAction,
|
useDataSource = useDefDataSource,
|
||||||
onChange,
|
useAction = useDefAction,
|
||||||
...others
|
onChange,
|
||||||
} = props;
|
...others
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
} = props;
|
||||||
useDataSource({
|
const [selectedRowKeys, setSelectedRowKeys] = useSelectedRowKeys();
|
||||||
onSuccess(data) {
|
useDataSource({
|
||||||
field.value = data?.data || [];
|
onSuccess(data) {
|
||||||
},
|
field.value = data?.data || [];
|
||||||
});
|
},
|
||||||
const { move } = useAction();
|
});
|
||||||
const restProps = {
|
const { move } = useAction();
|
||||||
rowSelection: props.rowSelection
|
const restProps = {
|
||||||
? {
|
rowSelection: props.rowSelection
|
||||||
|
? {
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
onChange(selectedRowKeys: any[]) {
|
onChange(selectedRowKeys: any[]) {
|
||||||
@ -189,39 +190,39 @@ export const TableArray: React.FC<any> = observer((props) => {
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
.nb-table-index {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
&:not(.checked) {
|
|
||||||
.nb-table-index {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.nb-table-index {
|
.nb-table-index {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.nb-origin-node {
|
&:not(.checked) {
|
||||||
display: block;
|
.nb-table-index {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
&:hover {
|
||||||
`,
|
.nb-table-index {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.nb-origin-node {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{dragSort && <SortHandle />}
|
{dragSort && <SortHandle />}
|
||||||
@ -232,13 +233,13 @@ export const TableArray: React.FC<any> = observer((props) => {
|
|||||||
'nb-origin-node',
|
'nb-origin-node',
|
||||||
checked ? 'checked' : null,
|
checked ? 'checked' : null,
|
||||||
css`
|
css`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 50%;
|
right: 50%;
|
||||||
transform: translateX(50%);
|
transform: translateX(50%);
|
||||||
&:not(.checked) {
|
&:not(.checked) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{originNode}
|
{originNode}
|
||||||
@ -248,43 +249,45 @@ export const TableArray: React.FC<any> = observer((props) => {
|
|||||||
},
|
},
|
||||||
...props.rowSelection,
|
...props.rowSelection,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultRowKey = (record: any) => {
|
const defaultRowKey = (record: any) => {
|
||||||
return field.value?.indexOf?.(record);
|
return field.value?.indexOf?.(record);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css`
|
className={css`
|
||||||
.ant-table {
|
.ant-table {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
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;
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<Table
|
<ReactDragListView
|
||||||
rowKey={defaultRowKey}
|
handleSelector={'.drag-handle'}
|
||||||
{...others}
|
onDragEnd={async (fromIndex, toIndex) => {
|
||||||
{...restProps}
|
const from = field.value[fromIndex];
|
||||||
components={components}
|
const to = field.value[toIndex];
|
||||||
columns={columns}
|
field.move(fromIndex, toIndex);
|
||||||
dataSource={field?.value?.slice?.()}
|
await move(from, to);
|
||||||
/>
|
}}
|
||||||
</ReactDragListView>
|
lineClassName={css`
|
||||||
</div>
|
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) => {
|
export const TableColumnActionBar = observer(
|
||||||
const Designer = useDesigner();
|
(props) => {
|
||||||
return (
|
const Designer = useDesigner();
|
||||||
<SortableItem className={designerCss}>
|
return (
|
||||||
<Designer />
|
<SortableItem className={designerCss}>
|
||||||
{props.children}
|
<Designer />
|
||||||
</SortableItem>
|
{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] : []);
|
const toArr = (value: any) => (isArr(value) ? value : isValid(value) ? [value] : []);
|
||||||
|
|
||||||
export const TableRowSelection = observer((props: Props) => {
|
export const TableRowSelection = observer(
|
||||||
const { rowKey = 'id', objectValue } = props;
|
(props: Props) => {
|
||||||
const field = useField<Field>();
|
const { rowKey = 'id', objectValue } = props;
|
||||||
const rowSelection: any = {
|
const field = useField<Field>();
|
||||||
type: 'checkbox',
|
const rowSelection: any = {
|
||||||
...props.rowSelection,
|
type: 'checkbox',
|
||||||
selectedRowKeys: toArr(field.value).map((val) => (typeof val === 'object' ? val[rowKey as any] : val)),
|
...props.rowSelection,
|
||||||
onChange(selectedRowKeys: any[], selectedRows?: any) {
|
selectedRowKeys: toArr(field.value).map((val) => (typeof val === 'object' ? val[rowKey as any] : val)),
|
||||||
if (rowSelection.type === 'checkbox') {
|
onChange(selectedRowKeys: any[], selectedRows?: any) {
|
||||||
props.onChange(objectValue ? selectedRows : selectedRowKeys);
|
if (rowSelection.type === 'checkbox') {
|
||||||
} else {
|
props.onChange(objectValue ? selectedRows : selectedRowKeys);
|
||||||
props.onChange([...(objectValue ? selectedRows : selectedRowKeys)].shift());
|
} else {
|
||||||
}
|
props.onChange([...(objectValue ? selectedRows : selectedRowKeys)].shift());
|
||||||
},
|
}
|
||||||
};
|
},
|
||||||
return <TableVoid {...props} rowSelection={rowSelection} />;
|
};
|
||||||
});
|
return <TableVoid {...props} rowSelection={rowSelection} />;
|
||||||
|
},
|
||||||
|
{ displayName: 'TableRowSelection' },
|
||||||
|
);
|
||||||
|
@ -80,45 +80,48 @@ const useDefSelectedRowKeys = () => {
|
|||||||
return [result?.state?.selectedRowKeys, (selectedRowKeys) => result?.setState?.({ selectedRowKeys })];
|
return [result?.state?.selectedRowKeys, (selectedRowKeys) => result?.setState?.({ selectedRowKeys })];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TableVoid: React.FC<TableVoidProps> = observer((props) => {
|
export const TableVoid: React.FC<TableVoidProps> = observer(
|
||||||
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
(props) => {
|
||||||
const field = useField<Field>();
|
const { rowKey = 'id', useDataSource = useDef, useSelectedRowKeys = useDefSelectedRowKeys } = props;
|
||||||
const fieldSchema = useFieldSchema();
|
const field = useField<Field>();
|
||||||
const form = useMemo(() => createForm(), []);
|
const fieldSchema = useFieldSchema();
|
||||||
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
const form = useMemo(() => createForm(), []);
|
||||||
const result = useDataSource(
|
const f = useAttach(form.createArrayField({ ...field.props, basePath: '' }));
|
||||||
{
|
const result = useDataSource(
|
||||||
uid: fieldSchema['x-uid'],
|
{
|
||||||
onSuccess(data) {
|
uid: fieldSchema['x-uid'],
|
||||||
form.setValues({
|
onSuccess(data) {
|
||||||
[fieldSchema.name]: data?.data,
|
form.setValues({
|
||||||
});
|
[fieldSchema.name]: data?.data,
|
||||||
if (field?.componentProps?.pagination === false) {
|
});
|
||||||
return;
|
if (field?.componentProps?.pagination === false) {
|
||||||
}
|
return;
|
||||||
field.componentProps.pagination = field.componentProps.pagination || {};
|
}
|
||||||
if (data?.meta?.count) {
|
field.componentProps.pagination = field.componentProps.pagination || {};
|
||||||
field.componentProps.pagination.total = data?.meta?.count;
|
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;
|
field.componentProps.pagination.current = data?.meta?.page || 1;
|
||||||
|
field.componentProps.pagination.pageSize = data?.meta?.pageSize || 10;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
props,
|
||||||
props,
|
);
|
||||||
);
|
return (
|
||||||
return (
|
<AsyncDataProvider value={result}>
|
||||||
<AsyncDataProvider value={result}>
|
<FormContext.Provider value={form}>
|
||||||
<FormContext.Provider value={form}>
|
<FieldContext.Provider value={f}>
|
||||||
<FieldContext.Provider value={f}>
|
<TableArray
|
||||||
<TableArray
|
{...props}
|
||||||
{...props}
|
rowKey={rowKey}
|
||||||
rowKey={rowKey}
|
useSelectedRowKeys={useSelectedRowKeys}
|
||||||
useSelectedRowKeys={useSelectedRowKeys}
|
loading={result?.loading}
|
||||||
loading={result?.loading}
|
pagination={usePaginationProps(props, result)}
|
||||||
pagination={usePaginationProps(props, result)}
|
/>
|
||||||
/>
|
</FieldContext.Provider>
|
||||||
</FieldContext.Provider>
|
</FormContext.Provider>
|
||||||
</FormContext.Provider>
|
</AsyncDataProvider>
|
||||||
</AsyncDataProvider>
|
);
|
||||||
);
|
},
|
||||||
});
|
{ displayName: 'TableVoid' },
|
||||||
|
);
|
||||||
|
@ -184,40 +184,43 @@ const schema: ISchema = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const DataSourceProvider = observer((props) => {
|
const DataSourceProvider = observer(
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
(props) => {
|
||||||
const [dataSource, setDataSource] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
const service = useRequest(
|
const [dataSource, setDataSource] = useState([]);
|
||||||
() => {
|
const service = useRequest(
|
||||||
return Promise.resolve({
|
() => {
|
||||||
data: [
|
return Promise.resolve({
|
||||||
{ id: 1, name: 'Name1' },
|
data: [
|
||||||
{ id: 2, name: 'Name2' },
|
{ id: 1, name: 'Name1' },
|
||||||
{ id: 3, name: 'Name3' },
|
{ id: 2, name: 'Name2' },
|
||||||
],
|
{ id: 3, name: 'Name3' },
|
||||||
});
|
],
|
||||||
},
|
});
|
||||||
{
|
|
||||||
onSuccess(data) {
|
|
||||||
setDataSource(data.data);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
);
|
onSuccess(data) {
|
||||||
console.log('dataSource1', dataSource);
|
setDataSource(data.data);
|
||||||
return (
|
},
|
||||||
<DataSourceContext.Provider
|
},
|
||||||
value={{
|
);
|
||||||
service,
|
console.log('dataSource1', dataSource);
|
||||||
dataSource,
|
return (
|
||||||
setDataSource,
|
<DataSourceContext.Provider
|
||||||
selectedRowKeys,
|
value={{
|
||||||
setSelectedRowKeys,
|
service,
|
||||||
}}
|
dataSource,
|
||||||
>
|
setDataSource,
|
||||||
{props.children}
|
selectedRowKeys,
|
||||||
</DataSourceContext.Provider>
|
setSelectedRowKeys,
|
||||||
);
|
}}
|
||||||
});
|
>
|
||||||
|
{props.children}
|
||||||
|
</DataSourceContext.Provider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'DataSourceProvider' },
|
||||||
|
);
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
|
@ -9,28 +9,31 @@ import { DndContext, SortableItem } from '../../common';
|
|||||||
import { useDesigner } from '../../hooks/useDesigner';
|
import { useDesigner } from '../../hooks/useDesigner';
|
||||||
import { TabsDesigner } from './Tabs.Designer';
|
import { TabsDesigner } from './Tabs.Designer';
|
||||||
|
|
||||||
export const Tabs: any = observer((props: TabsProps) => {
|
export const Tabs: any = observer(
|
||||||
const fieldSchema = useFieldSchema();
|
(props: TabsProps) => {
|
||||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
const fieldSchema = useFieldSchema();
|
||||||
return (
|
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||||
<DndContext>
|
return (
|
||||||
<AntdTabs
|
<DndContext>
|
||||||
style={props.style}
|
<AntdTabs
|
||||||
tabBarExtraContent={{
|
style={props.style}
|
||||||
right: render(),
|
tabBarExtraContent={{
|
||||||
}}
|
right: render(),
|
||||||
>
|
}}
|
||||||
{fieldSchema.mapProperties((schema, key) => {
|
>
|
||||||
return (
|
{fieldSchema.mapProperties((schema, key) => {
|
||||||
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
|
return (
|
||||||
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
|
||||||
</AntdTabs.TabPane>
|
<RecursionField name={key} schema={schema} onlyRenderProperties />
|
||||||
);
|
</AntdTabs.TabPane>
|
||||||
})}
|
);
|
||||||
</AntdTabs>
|
})}
|
||||||
</DndContext>
|
</AntdTabs>
|
||||||
);
|
</DndContext>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Tabs' },
|
||||||
|
);
|
||||||
|
|
||||||
const designerCss = css`
|
const designerCss = css`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -79,15 +82,18 @@ const designerCss = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
Tabs.TabPane = observer((props: TabPaneProps & { icon?: any }) => {
|
Tabs.TabPane = observer(
|
||||||
const Designer = useDesigner();
|
(props: TabPaneProps & { icon?: any }) => {
|
||||||
const field = useField();
|
const Designer = useDesigner();
|
||||||
return (
|
const field = useField();
|
||||||
<SortableItem className={classNames('nb-action-link', designerCss, props.className)}>
|
return (
|
||||||
{props.icon && <Icon style={{ marginRight: 2 }} type={props.icon} />} {props.tab || field.title}
|
<SortableItem className={classNames('nb-action-link', designerCss, props.className)}>
|
||||||
<Designer />
|
{props.icon && <Icon style={{ marginRight: 2 }} type={props.icon} />} {props.tab || field.title}
|
||||||
</SortableItem>
|
<Designer />
|
||||||
);
|
</SortableItem>
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'TabPane' },
|
||||||
|
);
|
||||||
|
|
||||||
Tabs.Designer = TabsDesigner;
|
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