refactor: new schema initializer and schema settings (#2802)
* fix: form * refactor: schema-initializer * fix: bug * refactor: schema initializer * refactor: rename * fix: delete SchemaInitializerProvider * refactor: props `insert` to hooks `useSchemaInitializerV2` * fix: bug * refactor: delete `SchemaInitializer.Button` * refactor: delete old SchemaInitializer * fix: bug * fix: workflow * fix: docs * fix: bug * fix: bug * feat: style * fix: remove v2 * fix: visible * fix: bug * fix: item hook * feat: item hook * fix: add search DataBlockInitializer * fix: build bug * fix: style bug * fix: style bug * fix: test bug * fix: test bug * fix: rerender bug * fix: remove menu select * fix: bug * chore: add aria-label for SchemaInitializerButton * refactor: rename name to camel case * fix: menu height bug * fix: build errors * fix: build errors * fix: bug * fix: bug * fix: performance * test: add test for header * fix: sidebar is not refresh (T-2422) * feat(e2e): support to add group page and link page * chore: make sure the page is configurable when using page.goto * test: add tests for menu initializer * fix: imporve code * chore: fix build error * chore: optimize locator of menu item * refactor: rename testid for select * test: make tests passing * fix: make tests passing * chore: upgrade vitest to v0.34.6 * chore: increase timeout of e2e * feat: core * fix: revert schema initializer demos * test: menu, page tabs, page grid, table column * fix: schema button interface * feat: refactor: page tab settings * feat: page settings * fix: dumirc * fix: export CSSVariableProvider * feat: lazy render * fix: form-item * fix: general schema desinger * feat: filter form item settings * refactor: form-v2 schema settings * refactor: form-v1 schema settings * refactor: action schema settings * fix: action bug * fix: form-item bug * fix: types error * docs: schema settings doc * docs: schema settings * feat: schema setting item add name * fix: visible lazy render bug * fix: revert form item filter * fix: test bug * fix: test JSON.parse bug * fix: test bug * fix: improve styling * fix: styling * fix: cleanup * fix: token.borderRadiusSM * fix: bug * test: add tests * fix: style bug * fix: add chart performance * feat: add SchemaDesignerContext * fix: bug * fix: test bug * style: create record action style improve * fix: make test passing * chore: mack tests passing * chore: make tests passing * test: fix tests * style: style revert * fix: bug * fix: data selector * fix: fix tests * fix: fix tests * fix: delete PluginManagerContext * refactor: improve router and add SchemaComponentProvider & CSSVariableProvider to MainComponent * fix: add dn and field builtin to SchemaSettingWrapper * feat: update docs * refactor: application providers * fix: test bug * fix: fix tests * chore: make test passing * feat: update docs * chore: rename collection name * feat: update docs * chore: skip weird test * fix: blockInitializers media to otherBlocks * fix: cancel to skip test * fix: bug * test: add test * refactor: migrate to small files * test: add tests for form block settings * chore: format * fix: add chart scroll bug * refactor: action designer improve * refactor: formitem designer schemaSetting * feat: schemaSettingsManager and schemaInitializerManager addItem and removeItem * test: add tests for color field in creating block * test: add tests for email field in creating block * test: make tests passing * perf: reduce fields number * fix: sub menu bug * test: add tests basic in editing form * test: add tests basic in details form * fix: improve code * test: make tests passing * test(plugin-mock-collections): add color for enum options * refactor: improve code * fix: bug * fix: bug * refactor: convert parameters to destructured object * test: add tests choices * test: add tests media * test: add tests for datetime in creating form * feat(plugin-mock-collection): generate faker time * test: add tests for datetime in editing form * test: add tests for datetime in details form * fix: bug * feat: improve code * test: add tests for relation fields * fix: rename SchemaSettings * fix: type bug * refactor: useDesinger() * fix: bug * fix: bug * fix: build tip * fix: designableState * fix: bug * fix: designable * fix: designable * test: add tests for relation fields * test: add tests for relation fields * test: add tests for relation fields * feat: client api doc * test: add tests for relation fields * test: avoid errors * test: make tests passing * fix: bug * test: make tests passing * test: add tests for advanced fields * test: increase e2e timeout-minutes to 60 * fix: bug * fix: improve code * feat: add schema initailizer component demos * test: make tests passing * fix: schema settings demos * feat: shallowMerge & deepMerge * test: reduce number of tests * test: make tests passing * feat: updates * fix: add Initializer Internal * demos: useSchemaSettingsRender * test: make tests passing * test: make tests passing * fix: improve docs * fix: bug * chore: upgrade dumi theme * test: make tests passing * test: add tests for linkage rules * test: add test for form data templates * test: add tests for default value * test: reduce number of tests * fix: dn.deepMerge * fix: bug * fix: bug * fix: toolbar * fix: docs ssr * test: add tests for system fields * test: add tests for actions * fix: bug * test: add tests for lazy loading of variables * test: make testing more stable * fix: update docs * fix: bug --------- Co-authored-by: Rain <958414905@qq.com> Co-authored-by: chenos <chenlinxh@gmail.com> Co-authored-by: katherinehhh <katherine_15995@163.com>
This commit is contained in:
parent
69abfc9832
commit
bf1a19426a
@ -30,6 +30,12 @@ export default defineConfig({
|
|||||||
alias: {
|
alias: {
|
||||||
...umiConfig.alias,
|
...umiConfig.alias,
|
||||||
},
|
},
|
||||||
|
ssr: {
|
||||||
|
|
||||||
|
},
|
||||||
|
exportStatic: {
|
||||||
|
ignorePreRenderError: true
|
||||||
|
},
|
||||||
cacheDirectoryPath: `node_modules/.docs-${lang}-cache`,
|
cacheDirectoryPath: `node_modules/.docs-${lang}-cache`,
|
||||||
outputPath: `./docs/dist/${lang}`,
|
outputPath: `./docs/dist/${lang}`,
|
||||||
resolve: {
|
resolve: {
|
||||||
|
6
.github/workflows/deploy-client-docs.yml
vendored
6
.github/workflows/deploy-client-docs.yml
vendored
@ -23,8 +23,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: "18"
|
node-version: "18"
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- name: Build
|
- name: Build zh-CN
|
||||||
run: yarn doc build core/client
|
run: yarn doc build core/client --lang=zh-CN
|
||||||
|
- name: Build en-US
|
||||||
|
run: yarn doc build core/client --lang=en-US
|
||||||
- name: Set tags
|
- name: Set tags
|
||||||
id: set-tags
|
id: set-tags
|
||||||
run: |
|
run: |
|
||||||
|
2
.github/workflows/nocobase-test-e2e.yml
vendored
2
.github/workflows/nocobase-test-e2e.yml
vendored
@ -76,4 +76,4 @@ jobs:
|
|||||||
DB_UNDERSCORED: ${{ matrix.underscored }}
|
DB_UNDERSCORED: ${{ matrix.underscored }}
|
||||||
DB_SCHEMA: ${{ matrix.schema }}
|
DB_SCHEMA: ${{ matrix.schema }}
|
||||||
COLLECTION_MANAGER_SCHEMA: ${{ matrix.collection_schema }}
|
COLLECTION_MANAGER_SCHEMA: ${{ matrix.collection_schema }}
|
||||||
timeout-minutes: 40
|
timeout-minutes: 60
|
||||||
|
@ -35,7 +35,6 @@ Add Providers, build-in Providers are:
|
|||||||
- SystemSettingsProvider
|
- SystemSettingsProvider
|
||||||
- PluginManagerProvider
|
- PluginManagerProvider
|
||||||
- SchemaComponentProvider
|
- SchemaComponentProvider
|
||||||
- SchemaInitializerProvider
|
|
||||||
- BlockSchemaComponentProvider
|
- BlockSchemaComponentProvider
|
||||||
- AntdSchemaComponentProvider
|
- AntdSchemaComponentProvider
|
||||||
- ACLProvider
|
- ACLProvider
|
||||||
|
@ -17,11 +17,3 @@ Used for the initialization of various schemas. Newly added schema can be insert
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The core of SchemaInitializer includes `<SchemaInitializer.Button />` and `<SchemaInitializer.Item />` the two components. `<SchemaInitializer.Button />` is used to create the dropdown menu button of schema, and the options of the dropdown menu is `<SchemaInitializer.Item/>`.
|
|
||||||
|
|
||||||
### `<SchemaInitializerProvider />`
|
|
||||||
|
|
||||||
### `<SchemaInitializer.Button />`
|
|
||||||
|
|
||||||
### `<SchemaInitializer.Item/>`
|
|
||||||
|
@ -10,7 +10,6 @@ Most of the extensions for the NocoBase client are provided as Providers.
|
|||||||
- SystemSettingsProvider
|
- SystemSettingsProvider
|
||||||
- PluginManagerProvider
|
- PluginManagerProvider
|
||||||
- SchemaComponentProvider
|
- SchemaComponentProvider
|
||||||
- SchemaInitializerProvider
|
|
||||||
- BlockSchemaComponentProvider
|
- BlockSchemaComponentProvider
|
||||||
- AntdSchemaComponentProvider
|
- AntdSchemaComponentProvider
|
||||||
- DocumentTitleProvider
|
- DocumentTitleProvider
|
||||||
|
@ -25,15 +25,11 @@
|
|||||||
|
|
||||||
```tsx |pure
|
```tsx |pure
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { SchemaInitializerContext } from '@nocobase/client';
|
import { Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
export default React.memo((props) => {
|
class MyPlugin extends Plugin {
|
||||||
const items = useContext(SchemaInitializerContext);
|
async load() {
|
||||||
const BlockInitializers = {};
|
this.app.schemaInitializerManager.add('BlockInitializers', BlockInitializers)
|
||||||
return (
|
}
|
||||||
<SchemaInitializerContext.Provider value={{ ...items, BlockInitializers }}>
|
}
|
||||||
{props.children}
|
|
||||||
</SchemaInitializerContext.Provider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
@ -35,7 +35,6 @@ const app = new Application({
|
|||||||
- SystemSettingsProvider
|
- SystemSettingsProvider
|
||||||
- PluginManagerProvider
|
- PluginManagerProvider
|
||||||
- SchemaComponentProvider
|
- SchemaComponentProvider
|
||||||
- SchemaInitializerProvider
|
|
||||||
- BlockSchemaComponentProvider
|
- BlockSchemaComponentProvider
|
||||||
- AntdSchemaComponentProvider
|
- AntdSchemaComponentProvider
|
||||||
- ACLProvider
|
- ACLProvider
|
||||||
|
@ -17,11 +17,3 @@
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
SchemaInitializer 的核心包括 `<SchemaInitializer.Button />` 和 `<SchemaInitializer.Item />` 两个组件。`<SchemaInitializer.Button />` 用于创建 Schema 的下拉菜单按钮,下拉菜单的菜单项为 `<SchemaInitializer.Item/>`。
|
|
||||||
|
|
||||||
### `<SchemaInitializerProvider />`
|
|
||||||
|
|
||||||
### `<SchemaInitializer.Button />`
|
|
||||||
|
|
||||||
### `<SchemaInitializer.Item/>`
|
|
||||||
|
@ -10,7 +10,6 @@ NocoBase 客户端的扩展大多以 Provider 的形式提供。
|
|||||||
- SystemSettingsProvider
|
- SystemSettingsProvider
|
||||||
- PluginManagerProvider
|
- PluginManagerProvider
|
||||||
- SchemaComponentProvider
|
- SchemaComponentProvider
|
||||||
- SchemaInitializerProvider
|
|
||||||
- BlockSchemaComponentProvider
|
- BlockSchemaComponentProvider
|
||||||
- AntdSchemaComponentProvider
|
- AntdSchemaComponentProvider
|
||||||
- DocumentTitleProvider
|
- DocumentTitleProvider
|
||||||
|
@ -25,15 +25,11 @@
|
|||||||
|
|
||||||
```tsx | pure
|
```tsx | pure
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { SchemaInitializerContext } from '@nocobase/client';
|
import { Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
export default React.memo((props) => {
|
class MyPlugin extends Plugin {
|
||||||
const items = useContext(SchemaInitializerContext);
|
async load() {
|
||||||
const BlockInitializers = {};
|
this.app.schemaInitializerManager.add('BlockInitializers', BlockInitializers)
|
||||||
return (
|
}
|
||||||
<SchemaInitializerContext.Provider value={{ ...items, BlockInitializers }}>
|
}
|
||||||
{props.children}
|
|
||||||
</SchemaInitializerContext.Provider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
278
docs/zh-CN/welcome/release/v15-changelog.md
Normal file
278
docs/zh-CN/welcome/release/v15-changelog.md
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
# v0.15
|
||||||
|
|
||||||
|
## 不兼容的变化
|
||||||
|
|
||||||
|
### SchemaInitializer 的注册和实现变更
|
||||||
|
|
||||||
|
#### 定义方式变更
|
||||||
|
|
||||||
|
以前 `SchemaInitializer` 支持 2 种定义方式,分别为对象和组件。例如:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const BlockInitializers = {
|
||||||
|
title: '{{t("Add block")}}',
|
||||||
|
icon: 'PlusOutlined',
|
||||||
|
items: [
|
||||||
|
// ...
|
||||||
|
],
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const BlockInitializers = () => {
|
||||||
|
return <SchemaInitializer.Button
|
||||||
|
title={'{{t("Add block")}}'}
|
||||||
|
icon={'PlusOutlined'}
|
||||||
|
items={[
|
||||||
|
// ...
|
||||||
|
]}
|
||||||
|
// ...
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
现在仅支持 `new SchemaInitializer()` 的实例。例如:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const blockInitializers = new SchemaInitializer({
|
||||||
|
name: 'BlockInitializers', // 名称,和原来保持一致
|
||||||
|
title: '{{t("Add block")}}',
|
||||||
|
icon: 'PlusOutlined',
|
||||||
|
items: [
|
||||||
|
// ...
|
||||||
|
],
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 参数变更
|
||||||
|
|
||||||
|
整体来说,`new SchemaInitializer()` 的参数参考了之前对象定义方式,但又有新的变更。具体如下:
|
||||||
|
|
||||||
|
- 新增 `name` 必填参数,用于 `x-initializer` 的值。
|
||||||
|
- 新增 `Component` 参数,用于定制化渲染的按钮。默认为 `SchemaInitializerButton`。
|
||||||
|
- 新增 `componentProps`、`style` 用于配置 `Component` 的属性和样式。
|
||||||
|
- 新增 `ItemsComponent` 参数,用于定制化渲染的列表。默认为 `SchemaInitializerItems`。
|
||||||
|
- 新增 `itemsComponentProps`、`itemsComponentStyle` 用于配置 `ItemsComponent` 的属性和样式。
|
||||||
|
- 新增 `popover` 参数,用于配置是否显示 `popover` 效果。
|
||||||
|
- 新增 `useInsert` 参数,用于当 `insert` 函数需要使用 hooks 时。
|
||||||
|
- 更改 将 `dropdown` 参数改为了 `popoverProps`,使用 `Popover` 代替了 `Dropdown`。
|
||||||
|
- items 参数变更
|
||||||
|
- 新增 `useChildren` 函数,用于动态控制子项。
|
||||||
|
- 更改 `visible` 参数改为了 `useVisible` 函数,用于动态控制是否显示。
|
||||||
|
- 更改 将 `component` 参数改为了 `Component`,用于列表项的渲染。
|
||||||
|
- 更改 将 `key` 参数改为了 `name`,用于列表项的唯一标识。
|
||||||
|
|
||||||
|
案例1:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- export const BlockInitializers = {
|
||||||
|
+ export const blockInitializers = new SchemaInitializer({
|
||||||
|
+ name: 'BlockInitializers',
|
||||||
|
'data-testid': 'add-block-button-in-page',
|
||||||
|
title: '{{t("Add block")}}',
|
||||||
|
icon: 'PlusOutlined',
|
||||||
|
wrap: gridRowColWrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
- key: 'dataBlocks',
|
||||||
|
+ name: 'data-blocks',
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: '{{t("Data blocks")}}',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
- key: 'table',
|
||||||
|
+ name: 'table',
|
||||||
|
- type: 'item', // 当有 Component 参数时,就不需要此了
|
||||||
|
title: '{{t("Table")}}',
|
||||||
|
- component: TableBlockInitializer,
|
||||||
|
+ Component: TableBlockInitializer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'form',
|
||||||
|
type: 'item',
|
||||||
|
title: '{{t("Form")}}',
|
||||||
|
component: FormBlockInitializer,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
案例2:
|
||||||
|
|
||||||
|
原来是组件定义的方式:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export const BulkEditFormItemInitializers = (props: any) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { insertPosition, component } = props;
|
||||||
|
const associationFields = useAssociatedFormItemInitializerFields({ readPretty: true, block: 'Form' });
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
data-testid="configure-fields-button-of-bulk-edit-form-item"
|
||||||
|
wrap={gridRowColWrap}
|
||||||
|
icon={'SettingOutlined'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: t('Display fields'),
|
||||||
|
children: useCustomBulkEditFormItemInitializerFields(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'item',
|
||||||
|
title: t('Add text'),
|
||||||
|
component: BlockItemInitializer,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
insertPosition={insertPosition}
|
||||||
|
component={component}
|
||||||
|
title={component ? null : t('Configure fields')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
现在需要改为 `new SchemaInitializer()` 的方式:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const bulkEditFormItemInitializers = new SchemaInitializer({
|
||||||
|
name: 'BulkEditFormItemInitializers',
|
||||||
|
'data-testid': 'configure-fields-button-of-bulk-edit-form-item',
|
||||||
|
wrap: gridRowColWrap,
|
||||||
|
icon: 'SettingOutlined',
|
||||||
|
// 原 insertPosition 和 component 是透传的,这里不用管,也是透传的
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: t('Display fields'),
|
||||||
|
name: 'display-fields', // 记得加上 name
|
||||||
|
useChildren: useCustomBulkEditFormItemInitializerFields, // 使用到了 useChildren
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('Add text'),
|
||||||
|
name: 'add-text',
|
||||||
|
Component: BlockItemInitializer, // component 替换为 Component
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
关于参数的具体说明可以参考 `SchemaInitializer` 的类型定义,以及 [SchemaInitializer 文档](https://client.docs.nocobase.com/client/schema-initializer)。
|
||||||
|
|
||||||
|
#### 实现原理变更
|
||||||
|
|
||||||
|
以前是将所有 `items` 转为 `Menu` 组件的 items JSON 对象,最后渲染成 Menu 列表。
|
||||||
|
|
||||||
|
现在默认情况下仅仅是渲染 `items` 列表项的 `Component` 组件,至于 `Component` 组件内部如何渲染取决于自身,最后也不会拼接成一个 JSON 对象。
|
||||||
|
|
||||||
|
具体说明参考 `SchemaInitializer` 的 [Nested items 示例](https://client.docs.nocobase.com/client/schema-initializer#nested-items)。
|
||||||
|
|
||||||
|
#### 列表中的组件获取参数方式变更
|
||||||
|
|
||||||
|
之前是通过 `props` 获取 `insert` 函数,现在需要通过 `useSchemaInitializer()` 获取。例如:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
const FormBlockInitializer = (props) => {
|
||||||
|
- const { insert } = props;
|
||||||
|
+ const { insert } = useSchemaInitializer();
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
export const blockInitializers = new SchemaInitializer({
|
||||||
|
name: 'BlockInitializers',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'form',
|
||||||
|
Component: FormBlockInitializer
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 注册方式变更
|
||||||
|
|
||||||
|
以前是通过 `SchemaInitializerProvider` 进行注册。例如:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<SchemaInitializerProvider
|
||||||
|
initializers={{ BlockInitializers }}
|
||||||
|
components={{ ManualActionDesigner }}
|
||||||
|
></SchemaInitializerProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
现在需要改为插件的方式。例如:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(blockInitializers);
|
||||||
|
this.app.addComponents({ ManualActionDesigner });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 修改方式变更
|
||||||
|
|
||||||
|
以前是通过 `SchemaInitializerContext` 获取到全部的 `Initializers` 然后进行增删改。例如下面代码是为了往 `BlockInitializers` 中的 `media` 下添加 `Hello`:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const items = useContext<any>(SchemaInitializerContext);
|
||||||
|
const mediaItems = items.BlockInitializers.items.find((item) => item.key === 'media');
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production' && !mediaItems) {
|
||||||
|
throw new Error('media block initializer not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = mediaItems.children;
|
||||||
|
if (!children.find((item) => item.key === 'hello')) {
|
||||||
|
children.push({
|
||||||
|
key: 'hello',
|
||||||
|
type: 'item',
|
||||||
|
title: '{{t("Hello block")}}',
|
||||||
|
component: HelloBlockInitializer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
新的方式则通过插件的方式更简洁的进行修改。例如:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 获取 BlockInitializers
|
||||||
|
const blockInitializers = this.app.schemaInitializerManager.get('BlockInitializers');
|
||||||
|
|
||||||
|
// 添加 Hello
|
||||||
|
blockInitializers.add('media.hello', {
|
||||||
|
title: '{{t("Hello block")}}',
|
||||||
|
Component: HelloBlockInitializer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用方式变更
|
||||||
|
|
||||||
|
之前使用 `useSchemaInitializer` 的方式进行渲染,现在需要改为 `useSchemaInitializerRender`,并且参数需要增加 `x-initializer-props`。例如:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||||
|
+ const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
||||||
|
|
||||||
|
render();
|
||||||
|
render({ style: { marginLeft: 8 } })
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
更多说明可以参考 [SchemaInitializer 文档](https://client.docs.nocobase.com/client/schema-initializer)。
|
@ -86,7 +86,7 @@
|
|||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
"commander": "^9.2.0",
|
"commander": "^9.2.0",
|
||||||
"dumi": "^2.2.0",
|
"dumi": "^2.2.0",
|
||||||
"dumi-theme-nocobase": "^0.2.14",
|
"dumi-theme-nocobase": "^0.2.18",
|
||||||
"eslint-plugin-jest-dom": "^5.0.1",
|
"eslint-plugin-jest-dom": "^5.0.1",
|
||||||
"eslint-plugin-testing-library": "^5.11.0",
|
"eslint-plugin-testing-library": "^5.11.0",
|
||||||
"execa": "^5.1.1",
|
"execa": "^5.1.1",
|
||||||
@ -103,7 +103,7 @@
|
|||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"typescript": "5.1.3",
|
"typescript": "5.1.3",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vitest": "^0.34.3"
|
"vitest": "^0.34.6"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "18.14.2",
|
"node": "18.14.2",
|
||||||
|
@ -24,7 +24,13 @@ export async function build(pkgs: string[]) {
|
|||||||
|
|
||||||
const packages = getPackages(pkgs);
|
const packages = getPackages(pkgs);
|
||||||
if (packages.length === 0) {
|
if (packages.length === 0) {
|
||||||
console.error(chalk.red(`[@nocobase/build]: '${pkgs.join(', ')}' not match any packages.`));
|
let msg = '';
|
||||||
|
if (pkgs.length) {
|
||||||
|
msg = `'${pkgs.join(', ')}' did not match any packages`
|
||||||
|
} else {
|
||||||
|
msg = 'No package matched'
|
||||||
|
}
|
||||||
|
console.warn(chalk.yellow(`[@nocobase/build]: ${msg}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,19 +3,29 @@ import { defineConfig } from 'dumi';
|
|||||||
import { defineThemeConfig } from 'dumi-theme-nocobase';
|
import { defineThemeConfig } from 'dumi-theme-nocobase';
|
||||||
|
|
||||||
const umiConfig = getUmiConfig();
|
const umiConfig = getUmiConfig();
|
||||||
|
process.env.DOC_LANG = process.env.DOC_LANG || 'zh-CN';
|
||||||
|
const lang = process.env.DOC_LANG;
|
||||||
|
|
||||||
|
console.log('process.env.DOC_LANG', lang);
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
hash: true,
|
hash: true,
|
||||||
alias: {
|
alias: {
|
||||||
...umiConfig.alias,
|
...umiConfig.alias,
|
||||||
},
|
},
|
||||||
|
// ssr: {},
|
||||||
|
// exportStatic: {
|
||||||
|
// ignorePreRenderError: true
|
||||||
|
// },
|
||||||
|
cacheDirectoryPath: `node_modules/.docs-client-${lang}-cache`,
|
||||||
|
outputPath: `./dist/${lang}`,
|
||||||
resolve: {
|
resolve: {
|
||||||
atomDirs: [
|
docDirs: [`./docs/${lang}`]
|
||||||
{ type: 'api', dir: 'src' },
|
|
||||||
{ type: 'api', dir: 'src/schema-component/antd' },
|
|
||||||
{ type: 'api', dir: 'src/route-switch/antd' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
locales: [
|
||||||
|
{ id: 'en-US', name: 'English' },
|
||||||
|
{ id: 'zh-CN', name: '中文' },
|
||||||
|
],
|
||||||
themeConfig: defineThemeConfig({
|
themeConfig: defineThemeConfig({
|
||||||
title: 'NocoBase',
|
title: 'NocoBase',
|
||||||
logo: 'https://www.nocobase.com/images/logo.png',
|
logo: 'https://www.nocobase.com/images/logo.png',
|
||||||
@ -25,92 +35,338 @@ export default defineConfig({
|
|||||||
nav: [
|
nav: [
|
||||||
{
|
{
|
||||||
title: 'API',
|
title: 'API',
|
||||||
link: '/apis/application',
|
link: '/core/application/application',
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// title: 'UI Schema',
|
||||||
|
// link: '/ui-schema',
|
||||||
|
// },
|
||||||
],
|
],
|
||||||
sidebarEnhance: {
|
sidebarEnhance: {
|
||||||
'/apis': [
|
'/core': [
|
||||||
{
|
{
|
||||||
title: 'Core',
|
title: 'Application',
|
||||||
type: 'group',
|
type: 'group',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: 'Application',
|
title: 'Application',
|
||||||
children: [
|
link: '/core/application/application',
|
||||||
{
|
|
||||||
title: 'Application',
|
|
||||||
link: '/apis/application',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'APIClient',
|
title: 'Plugin',
|
||||||
link: '/apis/api-client',
|
link: '/core/application/plugin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'PluginManager',
|
||||||
|
link: '/core/application/plugin-manager',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'RouterManager',
|
||||||
|
link: '/core/application/router-manager',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'PluginSettingsManager',
|
title: 'PluginSettingsManager',
|
||||||
link: '#',
|
link: '/core/application/plugin-settings-manager',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'UI schema designer',
|
title: 'UI Schema',
|
||||||
|
type: 'group',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: 'SchemaComponent',
|
title: 'SchemaComponent',
|
||||||
link: '#',
|
link: '/core/ui-schema/schema-component',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Designable',
|
||||||
|
link: '/core/ui-schema/designable',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'SchemaInitializer',
|
title: 'SchemaInitializer',
|
||||||
link: '#',
|
link: '/core/ui-schema/schema-initializer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'SchemaInitializerManager',
|
||||||
|
link: '/core/ui-schema/schema-initializer-manager',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'SchemaSettings',
|
title: 'SchemaSettings',
|
||||||
link: '#',
|
link: '/core/ui-schema/schema-settings',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'DNDContext & DragHandler',
|
title: 'SchemaSettingsManager',
|
||||||
link: '#',
|
link: '/core/ui-schema/schema-settings-manager',
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Collection Manager',
|
title: 'SchemaToolbar',
|
||||||
link: '#',
|
link: '/core/ui-schema/schema-toolbar',
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'BlockProvider',
|
|
||||||
link: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'RecordProvider',
|
|
||||||
link: '#',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'React components',
|
|
||||||
type: 'group',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'Board',
|
|
||||||
link: '#',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Icon',
|
|
||||||
link: '#',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Schema components',
|
|
||||||
type: 'group',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'Input',
|
|
||||||
link: '#',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
// '/ui-schema': [
|
||||||
|
// {
|
||||||
|
// title: 'Overview',
|
||||||
|
// link: '/ui-schema',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Globals',
|
||||||
|
// type: 'group',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Menu',
|
||||||
|
// link: '/ui-schema/globals/menu',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Page',
|
||||||
|
// link: '/ui-schema/globals/page',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Tabs',
|
||||||
|
// link: '/ui-schema/globals/tabs',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Blocks',
|
||||||
|
// type: 'group',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Overview',
|
||||||
|
// link: '/ui-schema/blocks',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Data blocks',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Overview',
|
||||||
|
// link: '/ui-schema/blocks/data',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Table',
|
||||||
|
// link: '/ui-schema/blocks/data/table',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Form',
|
||||||
|
// link: '/ui-schema/blocks/data/form',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Form(Read pretty)',
|
||||||
|
// link: '/ui-schema/blocks/data/form-read-pretty',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Details',
|
||||||
|
// link: '/ui-schema/blocks/data/details',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'List',
|
||||||
|
// link: '/ui-schema/blocks/data/list',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Grid Card',
|
||||||
|
// link: '/ui-schema/blocks/data/grid-card',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Calendar',
|
||||||
|
// link: '/ui-schema/blocks/data/calendar',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Kanban',
|
||||||
|
// link: '/ui-schema/blocks/data/kanban',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Map',
|
||||||
|
// link: '/ui-schema/blocks/data/map',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Gantt',
|
||||||
|
// link: '/ui-schema/blocks/data/gantt',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Charts',
|
||||||
|
// link: '/ui-schema/blocks/data/charts',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Filter blocks',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Collapse',
|
||||||
|
// link: '/ui-schema/blocks/filter/collapse',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Form',
|
||||||
|
// link: '/ui-schema/blocks/filter/form',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Other blocks',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'iframe',
|
||||||
|
// link: '/ui-schema/blocks/others/iframe',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Markdown',
|
||||||
|
// link: '/ui-schema/blocks/others/markdown',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Workflow todos',
|
||||||
|
// link: '/ui-schema/blocks/others/workflow-todo',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Fields',
|
||||||
|
// type: 'group',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Overview',
|
||||||
|
// link: '/ui-schema/fields',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'FormItem',
|
||||||
|
// link: '/ui-schema/fields/form-item',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'TableColumn',
|
||||||
|
// link: '/ui-schema/fields/table-column',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Association',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Title',
|
||||||
|
// link: '/ui-schema/fields/association-components/title',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Tag',
|
||||||
|
// link: '/ui-schema/fields/association-components/tag',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Select',
|
||||||
|
// link: '/ui-schema/fields/association-components/select',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'RecordPicker',
|
||||||
|
// link: '/ui-schema/fields/association-components/record-picker',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Cascader',
|
||||||
|
// link: '/ui-schema/fields/association-components/cascader-select',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Sub-form',
|
||||||
|
// link: '/ui-schema/fields/association-components/sub-form',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Sub-form(Popover)',
|
||||||
|
// link: '/ui-schema/fields/association-components/sub-form-popover',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Sub-details',
|
||||||
|
// link: '/ui-schema/fields/association-components/sub-details',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Sub-table',
|
||||||
|
// link: '/ui-schema/fields/association-components/cascader-select',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'File manager',
|
||||||
|
// link: '/ui-schema/fields/association-components/file-manager',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Actions',
|
||||||
|
// type: 'group',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// title: 'Overview',
|
||||||
|
// link: '/ui-schema/actions',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Add new',
|
||||||
|
// link: '/ui-schema/actions/add-new',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'View',
|
||||||
|
// link: '/ui-schema/actions/view',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Edit',
|
||||||
|
// link: '/ui-schema/actions/edit',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Delete',
|
||||||
|
// link: '/ui-schema/actions/delete',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Submit',
|
||||||
|
// link: '/ui-schema/actions/submit',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Filter',
|
||||||
|
// link: '/ui-schema/actions/filter',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Refresh',
|
||||||
|
// link: '/ui-schema/actions/refresh',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Print',
|
||||||
|
// link: '/ui-schema/actions/print',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Duplicate',
|
||||||
|
// link: '/ui-schema/actions/duplicate',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Export',
|
||||||
|
// link: '/ui-schema/actions/export',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Import',
|
||||||
|
// link: '/ui-schema/actions/import',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Bulk update',
|
||||||
|
// link: '/ui-schema/actions/bulk-update',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Bulk edit',
|
||||||
|
// link: '/ui-schema/actions/bulk-edit',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Add record(任意表)',
|
||||||
|
// link: '/ui-schema/actions/add-record',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Update record',
|
||||||
|
// link: '/ui-schema/actions/update-record',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Save record',
|
||||||
|
// link: '/ui-schema/actions/save-record',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Custom request',
|
||||||
|
// link: '/ui-schema/actions/custom-request',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: 'Submit to workflow',
|
||||||
|
// link: '/ui-schema/actions/submit-to-workflow',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/src
|
/src
|
||||||
/.dumi
|
/.dumi
|
||||||
|
/docs
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar: false
|
|
||||||
---
|
|
||||||
|
|
||||||
<code src="../src/application/demos/demo3.tsx"></code>
|
|
369
packages/core/client/docs/en-US/core/application/application.md
Normal file
369
packages/core/client/docs/en-US/core/application/application.md
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
# Application
|
||||||
|
|
||||||
|
## new Application(options)
|
||||||
|
|
||||||
|
创建一个 NocoBase 应用。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
export interface ApplicationOptions {
|
||||||
|
apiClient?: APIClientOptions;
|
||||||
|
ws?: WebSocketClientOptions | boolean;
|
||||||
|
i18n?: i18next;
|
||||||
|
providers?: (ComponentType | ComponentAndProps)[];
|
||||||
|
plugins?: PluginType[];
|
||||||
|
components?: Record<string, ComponentType>;
|
||||||
|
scopes?: Record<string, any>;
|
||||||
|
router?: RouterOptions;
|
||||||
|
schemaSettings?: SchemaSetting[];
|
||||||
|
schemaInitializers?: SchemaInitializer[];
|
||||||
|
loadRemotePlugins?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
- apiClient:API 请求实例,具体说明请参见:[https://docs.nocobase.com/api/sdk](https://docs.nocobase.com/api/sdk)
|
||||||
|
- i18n:国际化,具体请参考:[https://www.i18next.com/overview/api#createinstance](https://www.i18next.com/overview/api#createinstance)
|
||||||
|
- providers:上下文
|
||||||
|
- components:全局组件
|
||||||
|
- scopes:全局 scopes
|
||||||
|
- router:配置路由,具体请参考:[RouterManager](/core/application/router-manager)
|
||||||
|
- pluginSettings: [PluginSettingsManager](/core/application/plugin-settings-manager)
|
||||||
|
- schemaSettings:Schema 设置工具,具体参考:[SchemaSettingsManager](/core/ui-schema/schema-initializer-manager)
|
||||||
|
- schemaInitializers:Schema 添加工具,具体参考:[SchemaInitializerManager](/core/ui-schema/schema-initializer-manager)
|
||||||
|
- loadRemotePlugins:用于控制是否加载远程插件,默认为 `false`,即不加载远程插件(方便单测和 DEMO 环境)。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
|
const ProviderDemo = ({ children }) => {
|
||||||
|
return <div>
|
||||||
|
<div>hello world</div>
|
||||||
|
<div style={{ marginTop: 10 }}>{children}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load(){
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: () => <div>home page</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
providers: [ProviderDemo],
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实例属性
|
||||||
|
|
||||||
|
### app.i18n
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
i18n: i18next;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
详细介绍,请参考:[i18next](https://www.i18next.com/overview/api#createinstance)
|
||||||
|
|
||||||
|
### app.apiClient
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
apiClient: APIClient;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
详细介绍,请参考:[APIClient](https://docs.nocobase.com/api/sdk)
|
||||||
|
|
||||||
|
### app.router
|
||||||
|
|
||||||
|
详细介绍,请参考:[RouterManager](/core/application/router-manager)
|
||||||
|
|
||||||
|
### app.pluginSettingsManager
|
||||||
|
|
||||||
|
详细介绍,请参考:[PluginSettingsManager](/core/application/plugin-settings-manager)
|
||||||
|
|
||||||
|
### app.schemaSettingsManager
|
||||||
|
|
||||||
|
详细介绍,请参考:[SchemaSettingsManager](/core/ui-schema/schema-initializer-manager)
|
||||||
|
|
||||||
|
### app.schemaInitializerManager
|
||||||
|
|
||||||
|
详细介绍,请参考:[SchemaInitializerManager](/core/ui-schema/schema-initializer-manager)
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
### app.getRootComponent()
|
||||||
|
|
||||||
|
获取应用的根组件。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
getRootComponent(): React.FC
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { Application } from '@nocobase/client';
|
||||||
|
|
||||||
|
const app = new Application();
|
||||||
|
|
||||||
|
const App = app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
### app.mount()
|
||||||
|
|
||||||
|
将应用实例挂载在一个容器元素中。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
mount(containerOrSelector: Element | ShadowRoot | string): ReactDOM.Root
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { Application } from '@nocobase/client';
|
||||||
|
|
||||||
|
const app = new Application();
|
||||||
|
|
||||||
|
app.mount('#root');
|
||||||
|
```
|
||||||
|
|
||||||
|
### app.addProvider()
|
||||||
|
|
||||||
|
添加 `Provider` 上下文。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
addProvider<T = any>(component: ComponentType, props?: T): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
|
||||||
|
第一个参数是组件,第二个参数是组件的参数。注意 `Provider` 一定要渲染 `children`。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
// 场景1:第三方库,或者自己创建的 Context
|
||||||
|
const MyContext = createContext({});
|
||||||
|
app.addProvider(MyContext.provider, { value: { color: 'red' } });
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
import { Application, Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
|
const MyContext = createContext();
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const { color } = useContext(MyContext) || {};
|
||||||
|
return <div style={{ color }}>home page</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load(){
|
||||||
|
this.app.addProvider(MyContext.Provider, { value: { color: 'red' } });
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: HomePage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
// 场景2:自定义的组件,注意 children
|
||||||
|
const GlobalDemo = ({ name, children }) => {
|
||||||
|
return <div>
|
||||||
|
<div>hello, { name }</div>
|
||||||
|
<div>{ children }</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
app.addProvider(GlobalDemo, { name: 'nocobase' });
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Application, Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
|
const GlobalDemo = ({ name, children }) => {
|
||||||
|
return <div>
|
||||||
|
<div>hello, { name }</div>
|
||||||
|
<div>{ children }</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load(){
|
||||||
|
this.app.addProvider(GlobalDemo, { name: 'nocobase' });
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: () => <div>home page</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### app.addProviders()
|
||||||
|
|
||||||
|
添加多个 `Provider` 上下文。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
addProviders(providers: (ComponentType | [ComponentType, any])[]): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
|
||||||
|
一次添加多个 `Provider`。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
app.addProviders([[MyContext.provider, { value: { color: 'red' } }], [GlobalDemo, { name: 'nocobase' }]])
|
||||||
|
```
|
||||||
|
|
||||||
|
### app.addComponents()
|
||||||
|
|
||||||
|
添加全局组件。
|
||||||
|
|
||||||
|
全局组件可以使用在 [RouterManager](/core/application/router-manager) 和 [UI Schema](/core/ui-schema/schema-component)上。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
addComponents(components: Record<string, ComponentType>): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
app.addComponents({ Demo, Foo, Bar })
|
||||||
|
```
|
||||||
|
|
||||||
|
### app.addScopes()
|
||||||
|
|
||||||
|
添加全局的 scope。
|
||||||
|
|
||||||
|
全局组 scope 可以 [UI Schema](/core/ui-schema/schema-component) 上。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Application {
|
||||||
|
addScopes(scopes: Record<string, any>): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
function useSomeThing() {}
|
||||||
|
const anyVar = '';
|
||||||
|
|
||||||
|
app.addScopes({ useSomeThing, anyVar })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useApp()
|
||||||
|
|
||||||
|
获取当前应用的实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const useApp: () => Application
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const app = useApp();
|
||||||
|
return <div>{ JSON.stringify(app.router.getRouters()) }</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Application, Plugin, useApp } from '@nocobase/client';
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const app = useApp();
|
||||||
|
return <div>{ JSON.stringify(app.router.getRoutes()) }</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load(){
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: HomePage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
@ -0,0 +1,122 @@
|
|||||||
|
# PluginManager
|
||||||
|
|
||||||
|
用于管理插件。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginManager {
|
||||||
|
add<T = any>(plugin: typeof Plugin, opts?: PluginOptions<T>): Promise<void>
|
||||||
|
|
||||||
|
get<T extends typeof Plugin>(PluginClass: T): InstanceType<T>;
|
||||||
|
get<T extends {}>(name: string): T;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
### pluginManager.add()
|
||||||
|
|
||||||
|
将插件添加到应用中。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginManager {
|
||||||
|
add<T = any>(plugin: typeof Plugin, opts?: PluginOptions<T>): Promise<void>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
|
||||||
|
第一个参数是插件类,第二个则是实例化时传递的参数。前面已经讲过会在添加插件后,立即调用 `afterAdd` 钩子函数,所以返回的是 `Promise<void>`。
|
||||||
|
|
||||||
|
对于远程组件而言,会自动传递一个 `name` 参数。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPluginA extends Plugin {
|
||||||
|
async load() {
|
||||||
|
console.log('options', this.options)
|
||||||
|
console.log('app', this.app);
|
||||||
|
console.log('router', this.app.router, this.router);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPluginB extends Plugin {
|
||||||
|
// 需要在 afterAdd 执行添加的方法
|
||||||
|
async afterAdd() {
|
||||||
|
// 通过 `app.pluginManager.add()` 添加插件时,第一个参数是插件类,第二个参数是实例化时传递的参数
|
||||||
|
this.app.pluginManager.add(MyPluginA, { name: 'MyPluginA', hello: 'world' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [MyPluginB],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginManager.get()
|
||||||
|
|
||||||
|
获取插件实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginManager {
|
||||||
|
get<T extends typeof Plugin>(PluginClass: T): InstanceType<T>;
|
||||||
|
get<T extends {}>(name: string): T;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
|
||||||
|
可以通过 Class 获取插件示例,如果在插件注册的时候有 name,也可以通过字符串的 name 获取。
|
||||||
|
|
||||||
|
如果是远程插件,会自动传入 name,值为 package 的 name。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import MyPluginA from 'xxx';
|
||||||
|
|
||||||
|
class MyPluginB extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 方式1:通过 Class 获取
|
||||||
|
const myPluginA = this.app.pluginManager.get(MyPluginA);
|
||||||
|
|
||||||
|
// 方式2:通过 name 获取(添加的时候要传递 name 参数)
|
||||||
|
const myPluginA = this.app.pluginManager.get('MyPluginA');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
获取插件实例,等同于 `pluginManager.get()`。
|
||||||
|
|
||||||
|
### usePlugin()
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
function usePlugin<T extends typeof Plugin>(plugin: T): InstanceType<T>;
|
||||||
|
function usePlugin<T extends {}>(name: string): T;
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
|
||||||
|
可以通过 Class 获取插件示例,如果在插件注册的时候有 name,也可以通过字符串的 name 获取。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { usePlugin } from '@nocobase/client';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
// 通过 Class 获取
|
||||||
|
const myPlugin = usePlugin(MyPlugin);
|
||||||
|
|
||||||
|
// 通过 name 获取(添加的时候要传递 name 参数)
|
||||||
|
const myPlugin = usePlugin('MyPlugin');
|
||||||
|
|
||||||
|
return <div></div>
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,269 @@
|
|||||||
|
# PluginSettingsManager
|
||||||
|
|
||||||
|
![](../static/plugin-settings.jpg)
|
||||||
|
|
||||||
|
用于管理插件配置页面,其底层对应着 [RouterManager](/core/application/router-manager)。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface PluginSettingOptionsType {
|
||||||
|
title: string;
|
||||||
|
/**
|
||||||
|
* @default `Outlet`
|
||||||
|
*/
|
||||||
|
Component?: ComponentType<T> | string;
|
||||||
|
icon?: string;
|
||||||
|
/**
|
||||||
|
* sort, the smaller the number, the higher the priority
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
sort?: number;
|
||||||
|
aclSnippet?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PluginSettingsPageType {
|
||||||
|
label?: string;
|
||||||
|
title: string;
|
||||||
|
key: string;
|
||||||
|
icon: any;
|
||||||
|
path: string;
|
||||||
|
sort?: number;
|
||||||
|
name?: string;
|
||||||
|
isAllow?: boolean;
|
||||||
|
topLevelName?: string;
|
||||||
|
aclSnippet: string;
|
||||||
|
children?: PluginSettingsPageType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class PluginSettingsManager {
|
||||||
|
add(name: string, options: PluginSettingOptionsType): void
|
||||||
|
get(name: string, filterAuth?: boolean): PluginSettingsPageType;
|
||||||
|
getList(filterAuth?: boolean): PluginSettingsPageType[]
|
||||||
|
has(name: string): boolean;
|
||||||
|
remove(name: string): void;
|
||||||
|
getRouteName(name: string): string
|
||||||
|
getRoutePath(name: string): string;
|
||||||
|
hasAuth(name: string): boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
### pluginSettingsManager.add()
|
||||||
|
|
||||||
|
添加插件配置页。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
add(name: string, options: PluginSettingOptionsType): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
|
||||||
|
第一个参数 `name`,是路由唯一标识,用于后续的删改查,并且 `name` 支持 `.` 用于分割层级,不过需要注意当使用 `.` 分层的时候,父级要使用 [Outlet](https://reactrouter.com/en/main/components/outlet),让子元素能正常渲染。
|
||||||
|
|
||||||
|
第二个参数中 `Component` 支持组件形式和字符串形式,如果是字符串组件,要先通过 [app.addComponents](/core/application/application#appaddcomponents) 进行注册,具体参考 [RouterManager](/core/application/router-manager)。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
单层级配置。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const HelloSettingPage = () => {
|
||||||
|
return <div>hello setting page</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.pluginSettingsManager.add('hello', {
|
||||||
|
title: 'Hello', // menu title and page title
|
||||||
|
icon: 'ApiOutlined', // menu icon
|
||||||
|
Component: HelloSettingPage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
多层级配置。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
// 多层级配置页
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.pluginSettingsManager.add('hello', {
|
||||||
|
title: 'HelloWorld',
|
||||||
|
icon: '',
|
||||||
|
// Component: Outlet, 默认为 react-router-dom 的 Outlet 组件,可自定义
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app.pluginSettingsManager.add('hello.demo1', {
|
||||||
|
title: 'Demo1 Page',
|
||||||
|
Component: () => <div>Demo1 Page Content</div>
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app.pluginSettingsManager.add('hello.demo2', {
|
||||||
|
title: 'Demo2 Page',
|
||||||
|
Component: () => <div>Demo2 Page Content</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.get()
|
||||||
|
|
||||||
|
获取配置信息。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
get(name: string, filterAuth?: boolean): PluginSettingsPageType;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
|
||||||
|
第一个是在添加时的 name 参数,第二个参数是是否在获取的时候进行权限过滤。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
在组件中获取。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const app = useApp();
|
||||||
|
const helloSettingPage = this.app.pluginSettingsManager.get('hello');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在插件中获取。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const helloSettingPage = this.app.pluginSettingsManager.get('hello')
|
||||||
|
const helloSettingPage = this.app.pluginSettingsManager.get('hello', false);
|
||||||
|
|
||||||
|
const mobileAppConfigPage = this.app.pluginSettingsManager.get('mobile.app')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.getList()
|
||||||
|
|
||||||
|
获取插件配置页列表。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
getList(filterAuth?: boolean): PluginSettingsPageType[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
|
||||||
|
`filterAuth` 默认值为 `true`,即进行权限过滤。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const app = useApp();
|
||||||
|
const settings = app.pluginSettingsManager.getList();
|
||||||
|
const settings = app.pluginSettingsManager.getList(false);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.has()
|
||||||
|
|
||||||
|
判断是否存在,内部已进行权限过滤。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
has(name: string): boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.pluginSettingsManager.has('hello');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.remove()
|
||||||
|
|
||||||
|
移除配置。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
remove(name: string): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.getRouteName()
|
||||||
|
|
||||||
|
获取对应路由的名称。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
getRouteName(name: string): string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const helloRouteName = this.pluginSettingsManager.getRouteName('hello'); // admin.settings.hello
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.getRoutePath()
|
||||||
|
|
||||||
|
获取插件配置对应的页面路径。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
getRoutePath(name: string): string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const app = useApp();
|
||||||
|
const helloSettingPath = app.pluginSettingsManager.getRoutePath('hello');
|
||||||
|
|
||||||
|
return <div onClick={()=> navigate(helloSettingPath)}>
|
||||||
|
go to hello setting page
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pluginSettingsManager.hasAuth()
|
||||||
|
|
||||||
|
单独判断是否权限。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class PluginSettingsManager {
|
||||||
|
hasAuth(name: string): boolean;
|
||||||
|
}
|
||||||
|
```
|
85
packages/core/client/docs/en-US/core/application/plugin.md
Normal file
85
packages/core/client/docs/en-US/core/application/plugin.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Plugin
|
||||||
|
|
||||||
|
Plugin 基类。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Plugin<T = any> {
|
||||||
|
constructor(
|
||||||
|
protected options: T,
|
||||||
|
protected app: Application,
|
||||||
|
) {
|
||||||
|
this.options = options;
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pluginManager() {
|
||||||
|
return this.app.pluginManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
get router() {
|
||||||
|
return this.app.router;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pluginSettingsManager() {
|
||||||
|
return this.app.pluginSettingsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
get schemaInitializerManager() {
|
||||||
|
return this.app.schemaInitializerManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
get schemaSettingsManager() {
|
||||||
|
return this.app.schemaSettingsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
async afterAdd() {}
|
||||||
|
|
||||||
|
async beforeLoad() {}
|
||||||
|
|
||||||
|
async load() {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细信息
|
||||||
|
|
||||||
|
- 构造函数
|
||||||
|
- `options`: 插件的添加有两种方式,一种方式从插件列表中远程加载出来,另一种方式是通过 [PluginManager](/core/application/plugin-manager) 添加
|
||||||
|
- 远程加载:`options` 会被自动注入 `{ name: 'npm package.name' }`
|
||||||
|
- PluginManager `options` 由用户自己传递
|
||||||
|
- `app`:此参数是自动注入的,是应用实例
|
||||||
|
- 快捷访问:基类提供了对 `app` 部分方法和属性的快捷访问
|
||||||
|
- `pluginManager`
|
||||||
|
- `router`
|
||||||
|
- `pluginSettingsManager`
|
||||||
|
- `schemaSettingsManager`
|
||||||
|
- `schemaInitializerManager`
|
||||||
|
- 声明周期
|
||||||
|
- `afterAdd`:插件被添加后立即执行
|
||||||
|
- `beforeLoad`:执行渲染时执行,在 `afterAdd` 之后,`load` 之前
|
||||||
|
- `load`:最后执行
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
|
||||||
|
async afterAdd() {
|
||||||
|
console.log('afterAdd')
|
||||||
|
}
|
||||||
|
|
||||||
|
async beforeLoad() {
|
||||||
|
console.log('beforeLoad')
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
console.log('load')
|
||||||
|
|
||||||
|
// 可以访问应用实例
|
||||||
|
console.log(this.app)
|
||||||
|
|
||||||
|
// 访问应用实例内容
|
||||||
|
console.log(this.app.router, this.router);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,386 @@
|
|||||||
|
# RouterManager
|
||||||
|
|
||||||
|
用于管理路由。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { ComponentType } from 'react';
|
||||||
|
import { RouteObject } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface RouteType extends Omit<RouteObject, 'children' | 'Component'> {
|
||||||
|
Component?: ComponentType<T> | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterManager {
|
||||||
|
add(name: string, route: RouteType): void;
|
||||||
|
getRoutes(): Record<string, RouteType>;
|
||||||
|
getRoutesTree(): RouteObject[];
|
||||||
|
get(name: string): RouteType;
|
||||||
|
has(name: string): boolean;
|
||||||
|
remove(name: string): void;
|
||||||
|
setType(type: 'browser' | 'memory' | 'hash'): void;
|
||||||
|
setBasename(basename: string): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
### router.add()
|
||||||
|
|
||||||
|
添加一条路由。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
add(name: string, route: RouteType): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详情
|
||||||
|
|
||||||
|
第一个参数 `name`,是路由唯一标识,用于后续的删改查,并且 `name` 支持 `.` 用于分割层级,不过需要注意当使用 `.` 分层的时候,父级要使用 [Outlet](https://reactrouter.com/en/main/components/outlet),让子元素能正常渲染。
|
||||||
|
|
||||||
|
第二个参数 `RouteType` 的 `Component` 支持组件形式和字符串形式,如果是字符串组件,要先通过 [app.addComponents](/core/application/application#appaddcomponents) 进行注册。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
单层级路由。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: () => <div>home page</div>
|
||||||
|
})
|
||||||
|
this.app.router.add('login', {
|
||||||
|
path: '/login',
|
||||||
|
element: <div>login page</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { Plugin, Application } from '@nocobase/client';
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return <div>
|
||||||
|
<div>home page</div>
|
||||||
|
<button onClick={() => navigate('/login')}>GO To LoginPage</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoginPage = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return <div>
|
||||||
|
<div>login page</div>
|
||||||
|
<button onClick={() => navigate('/')}>GO To HomePage</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: HomePage
|
||||||
|
})
|
||||||
|
this.app.router.add('login', {
|
||||||
|
path: '/login',
|
||||||
|
Component: LoginPage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
多层级路由。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { Plugin } from '@nocobase/client';
|
||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
const AdminLayout = () =>{
|
||||||
|
return <div>
|
||||||
|
<div>This is admin layout</div>
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminSettings = () => {
|
||||||
|
return <div>This is admin settings page</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.add('admin', {
|
||||||
|
path: '/admin',
|
||||||
|
Component: AdminLayout
|
||||||
|
})
|
||||||
|
this.app.router.add('admin.settings', {
|
||||||
|
path: '/admin/settings',
|
||||||
|
Component: AdminSettings ,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useNavigate, Outlet } from 'react-router-dom';
|
||||||
|
import { Plugin, Application } from '@nocobase/client';
|
||||||
|
|
||||||
|
const AdminLayout = () =>{
|
||||||
|
return <div>
|
||||||
|
<div>This is admin layout</div>
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminSettings = () => {
|
||||||
|
return <div>This is admin settings page</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.add('admin', {
|
||||||
|
path: '/admin',
|
||||||
|
Component: AdminLayout
|
||||||
|
})
|
||||||
|
this.app.router.add('admin.settings', {
|
||||||
|
path: '/admin/settings',
|
||||||
|
Component: AdminSettings ,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/admin/settings'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
`Component` 参数为字符串。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const LoginPage = () => {
|
||||||
|
return <div>login page</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 通过 app.addComponents 进行注册
|
||||||
|
this.app.addComponents({ LoginPage })
|
||||||
|
|
||||||
|
this.app.router.add('login', {
|
||||||
|
path: '/login',
|
||||||
|
Component: 'LoginPage', // 这里可以使用字符串了
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.getRoutes()
|
||||||
|
|
||||||
|
获取路由列表。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
getRoutes(): Record<string, RouteType>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
console.log(this.app.router.getRoutes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
![](../static/CxjJbp0pcogYn6xviVyc6QT8nUg.png)
|
||||||
|
|
||||||
|
### router.getRoutesTree()
|
||||||
|
|
||||||
|
获取用于 [useRoutes()](https://reactrouter.com/hooks/use-routes) 的数据。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
getRoutesTree(): RouteObject[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const routes = this.app.router.getRoutesTree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.get()
|
||||||
|
|
||||||
|
获取单个路由配置。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
get(name: string): RouteType
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const adminRoute = this.app.router.get('admin')
|
||||||
|
const adminSettings = this.app.router.get('admin.settings')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.has()
|
||||||
|
|
||||||
|
判断是否添加过路由。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
has(name: string): boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const hasAdminRoute = this.app.router.has('admin')
|
||||||
|
const hasAdminSettings = this.app.router.has('admin.settings')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.remove()
|
||||||
|
|
||||||
|
移除路由配置。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
remove(name: string): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.remove('admin')
|
||||||
|
this.app.router.remove('admin.settings')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.setType()
|
||||||
|
|
||||||
|
设置路由类型,默认为 `browser`。
|
||||||
|
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
setType(type: 'browser' | 'memory' | 'hash'): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
- browser: [BrowserRouter](https://reactrouter.com/en/main/router-components/browser-router)
|
||||||
|
- memory: [MemoryRouter](https://reactrouter.com/en/main/router-components/hash-router)
|
||||||
|
- hash: [HashRouter](https://reactrouter.com/en/main/router-components/memory-router)
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.setType('hash')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### router.setBasename()
|
||||||
|
|
||||||
|
设置 [basename](https://reactrouter.com/en/main/router-components/browser-router#basename)。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class RouterManager {
|
||||||
|
setBasename(basename: string): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.router.setBasename('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useRouter()
|
||||||
|
|
||||||
|
获取当前路由的实例,等同于 `app.router`。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const useRouter: () => RouterManager
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { useRouter } from '@nocobase/client';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
}
|
||||||
|
```
|
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
packages/core/client/docs/en-US/core/static/designer.png
Normal file
BIN
packages/core/client/docs/en-US/core/static/designer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
packages/core/client/docs/en-US/core/static/plugin-settings.jpg
Normal file
BIN
packages/core/client/docs/en-US/core/static/plugin-settings.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 131 KiB |
@ -0,0 +1,122 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
|
||||||
|
const Hello = observer(() => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
|
{field.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function Demo() {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
// 调用插入功能
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: itemConfig.title,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add Block',
|
||||||
|
// 插入位置
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'Item B',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddBlockButton = observer(() => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
return render();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<AddBlockButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
components={{ Page, Hello, AddBlockButton }}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></SchemaComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.app.router.add('root', {
|
||||||
|
path: '/',
|
||||||
|
Component: Root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,38 @@
|
|||||||
|
import { ApplicationOptions, Grid, Plugin, SchemaComponent } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const HelloPage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'root',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginHello extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({ Grid });
|
||||||
|
this.router.add('hello', {
|
||||||
|
path: '/',
|
||||||
|
Component: HelloPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const appOptions: ApplicationOptions = {
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
plugins: [PluginHello],
|
||||||
|
};
|
||||||
|
|
||||||
|
export { appOptions };
|
@ -0,0 +1,130 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { Avatar } from 'antd';
|
||||||
|
|
||||||
|
const Hello = observer(() => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
|
{field.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function Demo() {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
// 调用插入功能
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: itemConfig.title,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add Block',
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
Component: (props: any) => (
|
||||||
|
<Avatar style={{ cursor: 'pointer' }} {...props}>
|
||||||
|
C
|
||||||
|
</Avatar>
|
||||||
|
),
|
||||||
|
componentProps: {
|
||||||
|
size: 'large',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'Item B',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddBlockButton = observer(() => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
return render();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<AddBlockButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
components={{ Page, Hello, AddBlockButton }}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></SchemaComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.app.router.add('root', {
|
||||||
|
path: '/',
|
||||||
|
Component: Root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaInitializer } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'A Group Title',
|
||||||
|
type: 'itemGroup',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'd',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'B Group Title',
|
||||||
|
type: 'itemGroup',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'B1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'B2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,71 @@
|
|||||||
|
import { Application, SchemaInitializer, SchemaInitializerItemGroup } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'A Group Title',
|
||||||
|
type: 'itemGroup',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'B Group Title',
|
||||||
|
type: 'itemGroup',
|
||||||
|
divider: true, // 渲染分割线
|
||||||
|
useChildren() {
|
||||||
|
// 动态子元素
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'b1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'B1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'B2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'c',
|
||||||
|
Component: () => {
|
||||||
|
return (
|
||||||
|
<SchemaInitializerItemGroup title="C Group Title">
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
name: 'c1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'C1',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
</SchemaInitializerItemGroup>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Grid,
|
||||||
|
SchemaInitializer,
|
||||||
|
useSchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
CardItem,
|
||||||
|
Application,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={'Demo-组件方式定义'} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
wrap: Grid.wrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo1',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'demo2',
|
||||||
|
type: 'item',
|
||||||
|
useComponentProps() {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: 'type 方式定义',
|
||||||
|
onClick: handleClick,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'demo3',
|
||||||
|
title: 'with items',
|
||||||
|
type: 'item',
|
||||||
|
onClick(args) {
|
||||||
|
console.log(args);
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'aaa',
|
||||||
|
value: 'aaa',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'bbb',
|
||||||
|
value: 'bbb',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello = () => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
return <h1>Hello, world! {schema.name}</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
components: { CardItem, Hello },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaInitializer } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'A subMenu',
|
||||||
|
type: 'subMenu',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'B subMenu',
|
||||||
|
type: 'subMenu',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'B1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'B2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { Application, Grid, SchemaInitializer, SchemaInitializerSelect, useDesignable } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const OpenModeSelect = () => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const openModeValue = fieldSchema?.['x-component-props']?.['openMode'] || 'drawer';
|
||||||
|
|
||||||
|
const { patch } = useDesignable();
|
||||||
|
const handleChange = (value) => {
|
||||||
|
// 修改当前节点的 Schema 的属性
|
||||||
|
patch({
|
||||||
|
'x-component-props': {
|
||||||
|
openMode: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaInitializerSelect
|
||||||
|
title={'Open mode'}
|
||||||
|
options={[
|
||||||
|
{ label: 'Drawer', value: 'drawer' },
|
||||||
|
{ label: 'Dialog', value: 'modal' },
|
||||||
|
]}
|
||||||
|
value={openModeValue}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
wrap: Grid.wrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'openMode',
|
||||||
|
Component: OpenModeSelect,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Grid,
|
||||||
|
SchemaInitializer,
|
||||||
|
Application,
|
||||||
|
SchemaInitializerSwitch,
|
||||||
|
useCurrentSchema,
|
||||||
|
useSchemaInitializer,
|
||||||
|
Action,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const actionKey = 'x-action';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
[actionKey]: 'create',
|
||||||
|
title: "{{t('Add new')}}",
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddNewButton = () => {
|
||||||
|
// 判断是否已插入
|
||||||
|
const { exists, remove } = useCurrentSchema(schema[actionKey], actionKey);
|
||||||
|
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
return (
|
||||||
|
<SchemaInitializerSwitch
|
||||||
|
checked={exists}
|
||||||
|
title={'Add new - Component方式'}
|
||||||
|
onClick={() => {
|
||||||
|
// 如果已插入,则移除
|
||||||
|
if (exists) {
|
||||||
|
return remove();
|
||||||
|
}
|
||||||
|
// 新插入子节点
|
||||||
|
insert(schema);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Configure actions',
|
||||||
|
wrap: Grid.wrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'Add New',
|
||||||
|
Component: AddNewButton,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Add New2',
|
||||||
|
type: 'switch',
|
||||||
|
useComponentProps() {
|
||||||
|
const { exists, remove } = useCurrentSchema(schema[actionKey], actionKey);
|
||||||
|
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
return {
|
||||||
|
checked: exists,
|
||||||
|
title: 'Add new - type 方式',
|
||||||
|
onClick() {
|
||||||
|
// 如果已插入,则移除
|
||||||
|
if (exists) {
|
||||||
|
return remove();
|
||||||
|
}
|
||||||
|
// 新插入子节点
|
||||||
|
insert(schema);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
components: { Action },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
import { Application, SchemaInitializer, SchemaInitializerItem, useSchemaInitializerItem } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
import { TableOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const { name, foo, icon } = useSchemaInitializerItem<{ name: string; foo: string; icon: ReactNode }>();
|
||||||
|
|
||||||
|
return <SchemaInitializerItem icon={icon} title={`${name} - ${foo}`} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
foo: 'bar',
|
||||||
|
icon: <TableOutlined />,
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,143 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerChild,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
SchemaInitializerItemsProps,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { ButtonProps, ListProps, List, Card } from 'antd';
|
||||||
|
|
||||||
|
const Hello = observer(() => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
|
{field.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function Demo() {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
// 调用插入功能
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: itemConfig.title,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomListGridMenu: FC<SchemaInitializerItemsProps<ButtonProps, ListProps<any>>> = (props) => {
|
||||||
|
const { items, options, ...others } = props;
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
{...others}
|
||||||
|
style={{ marginTop: 20 }}
|
||||||
|
dataSource={items}
|
||||||
|
grid={{ gutter: 16, column: 2 }}
|
||||||
|
renderItem={(item) => (
|
||||||
|
<List.Item style={{ minWidth: 100, textAlign: 'center' }}>
|
||||||
|
<SchemaInitializerChild {...item} />
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
></List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add Block',
|
||||||
|
// 插入位置
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
ItemsComponent: CustomListGridMenu,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'Item B',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddBlockButton = observer(() => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
return render();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<AddBlockButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
components={{ Page, Hello, AddBlockButton }}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></SchemaComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.app.router.add('root', {
|
||||||
|
path: '/',
|
||||||
|
Component: Root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaInitializer, SchemaInitializerItem } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: '静态 children',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A 1',
|
||||||
|
onClick: () => {
|
||||||
|
alert('a-1');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A 2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: '动态 children',
|
||||||
|
useChildren() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A 1',
|
||||||
|
onClick: () => {
|
||||||
|
alert('a-1');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A 2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Application, SchemaInitializer, SchemaInitializerItem } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
// 最终渲染 `SchemaInitializerItem`
|
||||||
|
return <SchemaInitializerItem title="Component Demo" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
Component: Demo, // 通过 Component 定义
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'item', // 通过 `type` 定义,底层对应着 `SchemaInitializerItem` 组件
|
||||||
|
title: 'type Demo',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Application, SchemaInitializer, SchemaInitializerItem } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const CommonDemo = (props) => {
|
||||||
|
return <SchemaInitializerItem title={props.title} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
Component: CommonDemo,
|
||||||
|
componentProps: {
|
||||||
|
title: 'componentProps',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
Component: CommonDemo,
|
||||||
|
useComponentProps() {
|
||||||
|
return {
|
||||||
|
title: 'useComponentProps',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaInitializer } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-initializer-common';
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'item',
|
||||||
|
title: 'Demo A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'item',
|
||||||
|
title: 'Demo B',
|
||||||
|
useVisible() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,111 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
useDesignable,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
|
||||||
|
const Hello = observer(() => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
|
{field.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const MyInitializerComponent = () => {
|
||||||
|
const { insertBeforeEnd } = useDesignable();
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
insertBeforeEnd({
|
||||||
|
type: 'void',
|
||||||
|
title: Math.random(),
|
||||||
|
'x-component': 'Hello',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Add block
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
popover: false,
|
||||||
|
Component: MyInitializerComponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddBlockButton = observer(() => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
return render();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<AddBlockButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
components={{ Page, Hello, AddBlockButton }}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></SchemaComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.app.router.add('root', {
|
||||||
|
path: '/',
|
||||||
|
Component: Root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,127 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
|
||||||
|
const Hello = observer(() => {
|
||||||
|
const field = useField<Field>();
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
|
{field.title}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function Demo() {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
// 调用插入功能
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: itemConfig.title,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add Block',
|
||||||
|
// 插入位置
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
title: 'Item B',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddBlockButton = observer(() => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>{render()}</div>
|
||||||
|
<div>可以进行参数的二次覆盖:{render({ style: { color: 'red' } })}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<AddBlockButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
components={{ Page, Hello, AddBlockButton }}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></SchemaComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.app.router.add('root', {
|
||||||
|
path: '/',
|
||||||
|
Component: Root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,48 @@
|
|||||||
|
import { ApplicationOptions, CardItem, Grid, Plugin, SchemaComponent } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Hello = () => {
|
||||||
|
return <h1>Hello, world!</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HelloPage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'root',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
properties: {
|
||||||
|
hello: Grid.wrap({
|
||||||
|
type: 'void',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginHello extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.router.add('hello', {
|
||||||
|
path: '/',
|
||||||
|
Component: HelloPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const appOptions: ApplicationOptions = {
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
components: { Grid, CardItem, Hello },
|
||||||
|
plugins: [PluginHello],
|
||||||
|
};
|
||||||
|
|
||||||
|
export { appOptions };
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
FormItem,
|
||||||
|
Input,
|
||||||
|
SchemaSettings,
|
||||||
|
SchemaSettingsActionModalItem,
|
||||||
|
useDesignable,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
import { ISchema, useField } from '@formily/react';
|
||||||
|
|
||||||
|
export const SchemaSettingsBlockTitleItem = function BlockTitleItem() {
|
||||||
|
const filed = useField();
|
||||||
|
const { patch } = useDesignable();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaSettingsActionModalItem
|
||||||
|
title={'Edit block title'}
|
||||||
|
schema={
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
title: 'Edit block title',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
title: 'Block title',
|
||||||
|
type: 'string',
|
||||||
|
default: filed.decoratorProps?.title,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ISchema
|
||||||
|
}
|
||||||
|
onSubmit={({ title }) => {
|
||||||
|
patch({
|
||||||
|
'x-decorator-props': {
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'blockTitle',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addComponents({ FormItem, Input });
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaSettings } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: 'group A',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: 'group B',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'b1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'B1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'B2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaSettings } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: 'group A',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: 'group B',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'b1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'B1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'B2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React, { FC, useState } from 'react';
|
||||||
|
import { Application, SchemaSettings, SchemaSettingsItem, useDesignable } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { Button, Input, Space } from 'antd';
|
||||||
|
|
||||||
|
const MarkdownEdit = () => {
|
||||||
|
const field = useField();
|
||||||
|
return (
|
||||||
|
<SchemaSettingsItem
|
||||||
|
title="Edit markdown"
|
||||||
|
onClick={() => {
|
||||||
|
field.editable = true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'markdown',
|
||||||
|
Component: MarkdownEdit,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello: FC<{ content?: string }> = observer((props) => {
|
||||||
|
const field = useField();
|
||||||
|
const { content } = props;
|
||||||
|
const [inputVal, setInputVal] = useState(content);
|
||||||
|
const { patch } = useDesignable();
|
||||||
|
return field.editable ? (
|
||||||
|
<Space>
|
||||||
|
<Input placeholder="input value" value={inputVal} onChange={(e) => setInputVal(e.target.value)} />
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
field.editable = false;
|
||||||
|
setInputVal(content);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
field.editable = false;
|
||||||
|
patch({
|
||||||
|
'x-component-props': {
|
||||||
|
content: inputVal,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
) : (
|
||||||
|
<h1>{content}</h1>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addComponents({ Hello });
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
FormItem,
|
||||||
|
Input,
|
||||||
|
SchemaSettings,
|
||||||
|
SchemaSettingsModalItem,
|
||||||
|
useSchemaSettings,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
export const SchemaSettingsBlockTitleItem = function BlockTitleItem() {
|
||||||
|
const { dn } = useSchemaSettings();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaSettingsModalItem
|
||||||
|
title={'Edit block title'}
|
||||||
|
schema={
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
title: 'Edit block title',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
title: 'Block title',
|
||||||
|
type: 'string',
|
||||||
|
default: dn.getSchemaAttribute('x-decorator-props.title'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-compile-omitted': ['default'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ISchema
|
||||||
|
}
|
||||||
|
onSubmit={({ title }) => {
|
||||||
|
dn.deepMerge({
|
||||||
|
'x-decorator-props': {
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'blockTitle',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addComponents({ FormItem, Input });
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaSettings } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
breakRemoveOn(s) {
|
||||||
|
return s['x-component'] === 'Grid'; // 其顶级是 Grid,这一层级不能删
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Application, SchemaSettings, SchemaSettingsSelectItem, useDesignable } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { Table } from 'antd';
|
||||||
|
|
||||||
|
const PageSize = () => {
|
||||||
|
const { patch } = useDesignable();
|
||||||
|
const filed = useField();
|
||||||
|
return (
|
||||||
|
<SchemaSettingsSelectItem
|
||||||
|
title={'Page Size'}
|
||||||
|
options={[
|
||||||
|
{ label: '5', value: 5 },
|
||||||
|
{ label: '10', value: 10 },
|
||||||
|
{ label: '15', value: 15 },
|
||||||
|
]}
|
||||||
|
value={filed.componentProps?.pageSize || 5}
|
||||||
|
onChange={(v) => {
|
||||||
|
patch({
|
||||||
|
'x-component-props': {
|
||||||
|
pageSize: v,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'pageSize',
|
||||||
|
Component: PageSize,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < 46; i++) {
|
||||||
|
data.push({
|
||||||
|
key: i,
|
||||||
|
name: `Edward King ${i}`,
|
||||||
|
age: 32,
|
||||||
|
address: `London, Park Lane no. ${i}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const Hello: FC<{ pageSize: number }> = observer((props) => {
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Age',
|
||||||
|
dataIndex: 'age',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Address',
|
||||||
|
dataIndex: 'address',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
pagination={{ pageSize: props.pageSize || 5, total: data.length }}
|
||||||
|
dataSource={data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addComponents({ Hello });
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaSettings } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'subMenu',
|
||||||
|
componentProps: {
|
||||||
|
title: 'Sub Menu A',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'subMenu',
|
||||||
|
componentProps: {
|
||||||
|
title: 'Sub Menu B',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'b1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'B1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'B2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Application, SchemaSettings, SchemaSettingsSwitchItem, useDesignable } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { Form, Input } from 'antd';
|
||||||
|
|
||||||
|
const FormItemRequired = () => {
|
||||||
|
const { patch } = useDesignable();
|
||||||
|
const filed = useField();
|
||||||
|
return (
|
||||||
|
<SchemaSettingsSwitchItem
|
||||||
|
title={'Required - 组件方式'}
|
||||||
|
checked={!!filed.componentProps?.required}
|
||||||
|
onChange={(v) => {
|
||||||
|
patch({
|
||||||
|
'x-component-props': {
|
||||||
|
required: v,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'required',
|
||||||
|
Component: FormItemRequired,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'required2',
|
||||||
|
type: 'switch',
|
||||||
|
useComponentProps() {
|
||||||
|
const { patch } = useDesignable();
|
||||||
|
const filed = useField();
|
||||||
|
return {
|
||||||
|
title: 'Required - type 方式',
|
||||||
|
checked: !!filed.componentProps?.required,
|
||||||
|
onChange(v) {
|
||||||
|
patch({
|
||||||
|
'x-component-props': {
|
||||||
|
required: v,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello: FC<{ required: boolean }> = observer((props) => {
|
||||||
|
return (
|
||||||
|
<Form>
|
||||||
|
<Form.Item name="note" label="Note" rules={[{ required: props.required }]}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.addComponents({ Hello });
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaSettings } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: '静态 children',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A 1',
|
||||||
|
onClick: () => {
|
||||||
|
alert('a-1');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'A 2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: '动态 children',
|
||||||
|
},
|
||||||
|
useChildren() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A 1',
|
||||||
|
onClick: () => {
|
||||||
|
alert('a-1');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'A 2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Application, SchemaSettings, SchemaSettingsItem } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
// 最终渲染 `SchemaInitializerItem`
|
||||||
|
return <SchemaSettingsItem title="Component Demo" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
Component: Demo, // 通过 Component 定义
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'item', // 通过 `type` 定义,底层对应着 `SchemaInitializerItem` 组件
|
||||||
|
componentProps: {
|
||||||
|
title: 'type Demo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Application, SchemaSettings, SchemaSettingsItem } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const CommonDemo = (props) => {
|
||||||
|
return <SchemaSettingsItem title={props.title} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
Component: CommonDemo,
|
||||||
|
componentProps: {
|
||||||
|
title: 'componentProps',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
Component: CommonDemo,
|
||||||
|
useComponentProps() {
|
||||||
|
return {
|
||||||
|
title: 'useComponentProps',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, SchemaSettings } from '@nocobase/client';
|
||||||
|
import { appOptions } from './schema-settings-common';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'Demo A',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'b',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'Demo B',
|
||||||
|
},
|
||||||
|
useVisible() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
...appOptions,
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,151 @@
|
|||||||
|
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
CardItem,
|
||||||
|
FormItem,
|
||||||
|
Grid,
|
||||||
|
Input,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaSettings,
|
||||||
|
SchemaSettingsModalItem,
|
||||||
|
createDesignable,
|
||||||
|
useAPIClient,
|
||||||
|
useSchemaComponentContext,
|
||||||
|
useSchemaSettings,
|
||||||
|
useSchemaSettingsRender,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
|
class PluginHello extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.router.add('hello', {
|
||||||
|
path: '/',
|
||||||
|
Component: HelloPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SchemaSettingsBlockTitleItem = function BlockTitleItem() {
|
||||||
|
const { dn } = useSchemaSettings();
|
||||||
|
console.log(dn.current);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaSettingsModalItem
|
||||||
|
title={'Edit block title'}
|
||||||
|
schema={
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
title: 'Edit block title',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
title: 'Block title',
|
||||||
|
type: 'string',
|
||||||
|
default: dn.getSchemaAttribute('x-decorator-props.title'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-compile-omitted': ['default'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ISchema
|
||||||
|
}
|
||||||
|
onSubmit={({ title }) => {
|
||||||
|
dn.shallowMerge({
|
||||||
|
'x-decorator-props': {
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'blockTitle',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello = (props) => {
|
||||||
|
return (
|
||||||
|
<h1>
|
||||||
|
Hello, world! <Demo />
|
||||||
|
{props.children}
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = useField();
|
||||||
|
const api = useAPIClient();
|
||||||
|
const { refresh } = useSchemaComponentContext();
|
||||||
|
const dn = useMemo(
|
||||||
|
() =>
|
||||||
|
createDesignable({
|
||||||
|
current: fieldSchema.parent,
|
||||||
|
model: field.parent,
|
||||||
|
api,
|
||||||
|
refresh,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], {
|
||||||
|
fieldSchema: dn.current,
|
||||||
|
field: dn.model,
|
||||||
|
dn,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>{render()}</div>
|
||||||
|
<div>可以进行参数的二次覆盖:{render({ style: { color: 'red' } })}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const HelloPage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'root',
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-decorator-props': {
|
||||||
|
title: 'aaa',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
schemaSettings: [mySettings],
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
components: { FormItem, Input, Grid, CardItem, Hello },
|
||||||
|
plugins: [PluginHello],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
791
packages/core/client/docs/en-US/core/ui-schema/designable.md
Normal file
791
packages/core/client/docs/en-US/core/ui-schema/designable.md
Normal file
@ -0,0 +1,791 @@
|
|||||||
|
# Designable
|
||||||
|
|
||||||
|
## Designable
|
||||||
|
|
||||||
|
对 Schema 节点进行增、删、改操作,并且提供了事件触发机制,用于将数据同步到服务端。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface Options {
|
||||||
|
current: Schema;
|
||||||
|
api?: APIClient;
|
||||||
|
onSuccess?: any;
|
||||||
|
refresh?: () => void;
|
||||||
|
t?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InsertAdjacentOptions {
|
||||||
|
wrap?: (s: ISchema) => ISchema;
|
||||||
|
removeParentsIfNoChildren?: boolean;
|
||||||
|
breakRemoveOn?: ISchema | BreakFn;
|
||||||
|
onSuccess?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Designable {
|
||||||
|
constructor(options: Options ) { }
|
||||||
|
loadAPIClientEvents(): void;
|
||||||
|
on(name: 'insertAdjacent' | 'remove' | 'error' | 'patch' | 'batchPatch', listener: any): void
|
||||||
|
emit(name: 'insertAdjacent' | 'remove' | 'error' | 'patch' | 'batchPatch', ...args: any[]): Promise<any>
|
||||||
|
refresh(): void
|
||||||
|
recursiveRemoveIfNoChildren(schema?: Schema, options?: RecursiveRemoveOptions): Schema;
|
||||||
|
remove(schema?: Schema, options?: RemoveOptions): Promise<any>
|
||||||
|
removeWithoutEmit(schema?: Schema, options?: RemoveOptions): Schema
|
||||||
|
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void | Promise<any>
|
||||||
|
insertBeforeBeginOrAfterEnd(schema: ISchema, options?: InsertAdjacentOptions): void
|
||||||
|
insertBeforeBegin(schema: ISchema, options?: InsertAdjacentOptions): void
|
||||||
|
insertAfterBegin(schema: ISchema, options?: InsertAdjacentOptions): void
|
||||||
|
insertBeforeEnd(schema: ISchema, options?: InsertAdjacentOptions): Promise<any>
|
||||||
|
insertAfterEnd(schema: ISchema, options?: InsertAdjacentOptions): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 构造函数
|
||||||
|
|
||||||
|
- 参数讲解
|
||||||
|
|
||||||
|
- `current`:需要操作的 Schema 节点
|
||||||
|
- `api`:用于发起后端请求的 [APIClient](https://docs.nocobase.com/api/sdk) 实例
|
||||||
|
- `onSuccess`:后端接口请求成功后的回调
|
||||||
|
- `refresh`:用于更新节点后,刷新页面
|
||||||
|
- `t`:`useTranslation()` 的返回值
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'hello',
|
||||||
|
'x-component': 'div',
|
||||||
|
})
|
||||||
|
|
||||||
|
const dn = new Designable({ current: schema });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schema 操作方法
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.b;
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(schema.toJSON());
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { createDesignable } from '@nocobase/client';
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.properties['b'];
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### remove
|
||||||
|
|
||||||
|
移除当前节点
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
dn.remove();
|
||||||
|
console.log(schema.toJSON());
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { createDesignable } from '@nocobase/client';
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.properties['b'];
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
});
|
||||||
|
|
||||||
|
dn.remove();
|
||||||
|
|
||||||
|
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
|
||||||
|
```
|
||||||
|
|
||||||
|
```diff
|
||||||
|
{
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
- b: {
|
||||||
|
- type: 'void',
|
||||||
|
- properties: {
|
||||||
|
- c: {
|
||||||
|
- type: 'void',
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### insertBeforeBegin
|
||||||
|
|
||||||
|
在当前节点的前面插入,并会触发 `insertAdjacent` 事件。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
dn.insertBeforeBegin({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
console.log(schema.toJSON());
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { createDesignable } from '@nocobase/client';
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.properties['b'];
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
});
|
||||||
|
|
||||||
|
dn.insertBeforeBegin({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
|
||||||
|
```
|
||||||
|
|
||||||
|
```diff
|
||||||
|
{
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
+ d: {
|
||||||
|
+ type: 'void',
|
||||||
|
+ },
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### insertAfterBegin
|
||||||
|
|
||||||
|
在当前节点的前面插入,并会触发 `insertAdjacent` 事件。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
dn.insertAfterBegin({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
console.log(schema.toJSON());
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { createDesignable } from '@nocobase/client';
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.properties['b'];
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
});
|
||||||
|
|
||||||
|
dn.insertAfterBegin({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
|
||||||
|
```
|
||||||
|
|
||||||
|
```diff
|
||||||
|
{
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
+ d: {
|
||||||
|
+ type: 'void',
|
||||||
|
+ },
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### insertBeforeEnd
|
||||||
|
|
||||||
|
在当前节点的前面插入,并会触发 `insertAdjacent` 事件。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
dn.insertBeforeEnd({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
console.log(schema.toJSON());
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { createDesignable } from '@nocobase/client';
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.properties['b'];
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
});
|
||||||
|
|
||||||
|
dn.insertBeforeEnd({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
|
||||||
|
```
|
||||||
|
|
||||||
|
```diff
|
||||||
|
{
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
+ d: {
|
||||||
|
+ type: 'void',
|
||||||
|
+ },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### insertAfterEnd
|
||||||
|
|
||||||
|
在当前节点的前面插入,并会触发 `insertAdjacent` 事件。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
dn.insertAfterEnd({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
console.log(schema.toJSON());
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { createDesignable } from '@nocobase/client';
|
||||||
|
|
||||||
|
const schema = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const b = schema.properties['b'];
|
||||||
|
|
||||||
|
const dn = createDesignable({
|
||||||
|
current: b,
|
||||||
|
});
|
||||||
|
|
||||||
|
dn.insertAfterEnd({
|
||||||
|
type: 'void',
|
||||||
|
name: 'd',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
|
||||||
|
```
|
||||||
|
|
||||||
|
```diff
|
||||||
|
{
|
||||||
|
type: 'void',
|
||||||
|
name: 'a',
|
||||||
|
properties: {
|
||||||
|
b: {
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
c: {
|
||||||
|
type: 'void',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
+ d: {
|
||||||
|
+ type: 'void',
|
||||||
|
+ },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### insertAdjacent
|
||||||
|
|
||||||
|
根据第一个参数决定插入的位置,是前面四个方法的封装。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class Designable {
|
||||||
|
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void | Promise<any>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件监听和 API 请求
|
||||||
|
|
||||||
|
- `on` :添加事件监听的基础方法
|
||||||
|
- `loadAPIClientEvents`:调用 `on` 方法添加对 `insertAdjacent`、`patch`、`batchPatch`、`remove` 的事件的监听,主要功能是将变更的 Schema 更新到服务端
|
||||||
|
- `emit`:是根据事件名称,调用之前注册过的方法,具体是由前面讲过的 *插入操作和删除操作* 触发
|
||||||
|
|
||||||
|
而 `loadAPIClientEvents()` 并非在初始化时调用,需要手动调用,换而言之,如果不调用 `dn.loadAPIClientEvents()`,则不会将更新发送到服务端,主要是简化在单测或者 DEMO 环境对服务端的 Mock。
|
||||||
|
|
||||||
|
## 工具函数
|
||||||
|
|
||||||
|
### createDesignable()
|
||||||
|
|
||||||
|
对 `new Designable()` 的简单封装。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
function createDesignable(options: CreateDesignableProps) {
|
||||||
|
return new Designable(options);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const dn = createDesignable({ current: schema });
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useFieldSchema()
|
||||||
|
|
||||||
|
用户获取当前节点 Schema JSON 对象,更多信息请参考 [formily useFieldSchema()](https://react.formilyjs.org/api/hooks/use-field-schema)。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
const useFieldSchema: () => Schema;
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { Application, Plugin, SchemaComponent } from '@nocobase/client';
|
||||||
|
const Demo = ({ children }) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
return <div style={{ border: '1px solid red' }}>
|
||||||
|
<pre>{ JSON.stringify(fieldSchema, null, 2)}</pre>
|
||||||
|
<div style={{ paddingLeft: 20 }}>{children}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'hello',
|
||||||
|
'x-component': 'Demo', // 这里是 Demo 组件
|
||||||
|
'properties': {
|
||||||
|
'world': {
|
||||||
|
'type': 'void',
|
||||||
|
'x-component': 'Demo', // 这里也是 Demo 组件
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return <SchemaComponent components={{ Demo }} schema={schema} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
providers: [Root]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
### useField()
|
||||||
|
|
||||||
|
获取当前节点 Schema 实例,更多信息请参考 [formily useField()](https://react.formilyjs.org/api/hooks/use-field)
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
import { GeneralField } from '@formily/core';
|
||||||
|
const useField: <T = GeneralField>() => T;
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const field = useField();
|
||||||
|
console.log('field', field);
|
||||||
|
return <div></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'hello',
|
||||||
|
'x-component': 'Demo', // 这里是 Demo 组件
|
||||||
|
'properties': {
|
||||||
|
'world': {
|
||||||
|
'type': 'void',
|
||||||
|
'x-component': 'Demo', // 这里也是 Demo 组件
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return <SchemaComponent components={{ Hello }} schema={schema} />
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### useDesignable()
|
||||||
|
|
||||||
|
对当前 Schema 节点的修改操作。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface InsertAdjacentOptions {
|
||||||
|
wrap?: (s: ISchema) => ISchema;
|
||||||
|
removeParentsIfNoChildren?: boolean;
|
||||||
|
breakRemoveOn?: ISchema | BreakFn;
|
||||||
|
onSuccess?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
|
||||||
|
|
||||||
|
function useDesignable(): {
|
||||||
|
dn: Designable;
|
||||||
|
designable: boolean;
|
||||||
|
reset: () => void;
|
||||||
|
refresh: () => void;
|
||||||
|
setDesignable: (value: boolean) => void;
|
||||||
|
findComponent(component: any): any;
|
||||||
|
patch: (key: ISchema | string, value?: any) => void
|
||||||
|
on(name: "error" | "insertAdjacent" | "remove" | "patch" | "batchPatch", listener: any): void
|
||||||
|
remove(schema?: any, options?: RemoveOptions): void
|
||||||
|
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void
|
||||||
|
insertBeforeBegin(schema: ISchema): void;
|
||||||
|
insertAfterBegin(schema: ISchema): void;
|
||||||
|
insertBeforeEnd(schema: ISchema): void;
|
||||||
|
insertAfterEnd(schema: ISchema): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
|
||||||
|
- designable、reset、refresh、setDesignable:这些值继承自 [SchemaComponentContext](https://www.baidu.com)
|
||||||
|
- dn:是 `Designable` 的实例
|
||||||
|
- findComponent:用于查找 Schema 中字符串对应真正的组件,如果组件未注册则返回 `null`
|
||||||
|
- remove:内部调用的是 `dn.remove` 方法
|
||||||
|
- on:内部调用的是 `dn.on` 方法
|
||||||
|
- insertAdjacent:插入新的 Schema 节点,内部调用的是 `dn.insertAdjacent` 方法
|
||||||
|
- position:插入位置
|
||||||
|
- schema:新的 Schema 节点
|
||||||
|
- Options
|
||||||
|
- wrap:对 Schema 的二次处理的回调函数
|
||||||
|
- removeParentsIfNoChildren:当没有子元素时,删除父元素
|
||||||
|
- breakRemoveOn:停止删除的判断回调
|
||||||
|
- onSuccess:插入成功的回调
|
||||||
|
- insertBeforeBegin:内部调用的是 `dn.insertBeforeBegin` 方法
|
||||||
|
- insertAfterBegin:内部调用的是 `dn.insertAfterBegin` 方法
|
||||||
|
- insertBeforeEnd:内部调用的是 `dn.insertBeforeEnd` 方法
|
||||||
|
- insertAfterEnd:内部调用的是 `dn.insertAfterEnd` 方法
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
插入节点。
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
SchemaComponentProvider,
|
||||||
|
SchemaComponent,
|
||||||
|
useDesignable,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
const Hello = observer(
|
||||||
|
(props) => {
|
||||||
|
const { insertAdjacent } = useDesignable();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{fieldSchema.name}</h1>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
insertAdjacent('beforeBegin', {
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
before begin
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
insertAdjacent('afterBegin', {
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
after begin
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
insertAdjacent('beforeEnd', {
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
before end
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
insertAdjacent('afterEnd', {
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
after end
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
<div style={{ margin: 50 }}>{props.children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Hello' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return <div>{props.children}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Page, Hello }}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
部分更新。
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
SchemaComponentProvider,
|
||||||
|
SchemaComponent,
|
||||||
|
useDesignable,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { observer, Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { FormItem } from '@formily/antd-v5';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
const Hello = observer(
|
||||||
|
(props) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = useField();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{field.title}</h1>
|
||||||
|
{ JSON.stringify(props) }
|
||||||
|
<br/>
|
||||||
|
{ JSON.stringify(field.componentProps) }
|
||||||
|
<br/>
|
||||||
|
{ JSON.stringify(field.decoratorProps) }
|
||||||
|
<br/>
|
||||||
|
{ JSON.stringify(fieldSchema.toJSON()) }
|
||||||
|
<br/>
|
||||||
|
<Button onClick={() => {
|
||||||
|
dn.shallowMerge({
|
||||||
|
title: uid(),
|
||||||
|
'x-component-props': {a: uid(), },
|
||||||
|
'x-decorator-props': {b: uid(), },
|
||||||
|
});
|
||||||
|
}}>shallowMerge</Button>
|
||||||
|
<Button onClick={() => {
|
||||||
|
dn.deepMerge({
|
||||||
|
title: uid(),
|
||||||
|
'x-component-props': {c: uid() },
|
||||||
|
'x-decorator-props': {d: uid(), },
|
||||||
|
});
|
||||||
|
}}>deepMerge</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Hello' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return <div>{props.children}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ FormItem, Page, Hello }}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'Title 1',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
@ -0,0 +1,208 @@
|
|||||||
|
# SchemaComponent
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
### SchemaComponentContext
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaComponentContext {
|
||||||
|
scope?: any;
|
||||||
|
components?: SchemaReactComponents;
|
||||||
|
refresh?: () => void;
|
||||||
|
reset?: () => void;
|
||||||
|
designable?: boolean;
|
||||||
|
setDesignable?: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Schema 渲染的上下文。
|
||||||
|
|
||||||
|
- `scope`:Schema 中变量的映射
|
||||||
|
- `components`: Schema 中组件的映射
|
||||||
|
- `refresh`:触发 React 重新渲染的工具函数
|
||||||
|
- `reset`:重置整个 Schema 节点
|
||||||
|
- `designable`:是否显示设计器,默认 `false`
|
||||||
|
- `setDesignable`:用于切换 `designable` 的值
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useSchemaComponentContext()
|
||||||
|
|
||||||
|
用于获取 `SchemaComponentContext` 的值,是 `useContext(SchemaComponentContext)` 的封装。
|
||||||
|
|
||||||
|
## 组件
|
||||||
|
|
||||||
|
### SchemaComponentProvider
|
||||||
|
|
||||||
|
其是对 `SchemaComponentContext.Provider` 和 [FormProvider ](https://react.formilyjs.org/api/components/form-provider)的封装,并内置在 `Application` 中,并且会将 `app.components` 和 `app.scopes` 传递过去,所以一般情况下 *不需要关注* 此组件。
|
||||||
|
|
||||||
|
- props
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaComponentProviderProps {
|
||||||
|
designable?: boolean;
|
||||||
|
form?: Form;
|
||||||
|
scope?: any;
|
||||||
|
components?: SchemaReactComponents;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
- `designable`:`SchemaComponentContext` 中 `designable` 的默认值
|
||||||
|
- `form`:NocoBase 的 Schema 能力是基于 formily 的 `FormProvider` 提供的,form 是其参数,默认为 `createForm()`
|
||||||
|
- `scope`:Schema 中所用到的变量,会通过 `SchemaComponentContext` 进行传递
|
||||||
|
- `components`:Schema 中所用到的组件,会通过 `SchemaComponentContext` 进行传递
|
||||||
|
|
||||||
|
### SchemaComponent
|
||||||
|
|
||||||
|
用于渲染 Schema,此组件必须和 `SchemaComponentProvider` 一起使用,因为 `SchemaComponentProvider` 提供了 [FormProvider](https://react.formilyjs.org/api/components/form-provider) 作为渲染 Schema 的根节点。
|
||||||
|
|
||||||
|
- Props
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
type SchemaComponentProps = (ISchemaFieldProps | IRecursionFieldProps) & {
|
||||||
|
memoized?: boolean;
|
||||||
|
components?: SchemaReactComponents;
|
||||||
|
scope?: any;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
|
||||||
|
- `memoized`:当为 `true` 时,会对每层的 Schema 使用 `useMemo()` 进行处理
|
||||||
|
- `components`:同 `SchemaComponentProvider` 的 `components`
|
||||||
|
- `scope`: 同 `SchemaComponentProvider` 的 `components`
|
||||||
|
|
||||||
|
## 综合示例
|
||||||
|
|
||||||
|
结合 `SchemaComponentProvider`、 `useSchemaComponentContext()` 和 `SchemaComponent`。
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { SchemaComponentProvider, useSchemaComponentContext, SchemaComponent, } from '@nocobase/client';
|
||||||
|
const Hello = () => {
|
||||||
|
const { designable, setDesignable } = useSchemaComponentContext();
|
||||||
|
return <div>
|
||||||
|
<div style={{ padding: 20, border: designable ? '1px solid red' : undefined }} contentEditable={designable}>hello world</div>
|
||||||
|
<button onClick={() => setDesignable(!designable) }>change designable</button>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'hello',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
}
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
return <SchemaComponent schema={schema} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return <SchemaComponentProvider components={{ Hello }}>
|
||||||
|
<Demo />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Root;
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 `new Application()` 的方式,其内置了 `SchemaComponentProvider` ,我们可以如下操作:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { Application, Plugin, useSchemaComponentContext, SchemaComponent } from '@nocobase/client';
|
||||||
|
const Hello = () => {
|
||||||
|
const { designable, setDesignable } = useSchemaComponentContext();
|
||||||
|
return <div>
|
||||||
|
<div style={{ padding: 20, border: designable ? '1px solid red' : undefined }} contentEditable={designable}>hello world</div>
|
||||||
|
<button onClick={() => setDesignable(!designable) }>change designable</button>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'hello',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
}
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
return <SchemaComponent components={{ Hello }} schema={schema} />
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({ Hello });
|
||||||
|
this.app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: HomePage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [MyPlugin],
|
||||||
|
})
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
### SchemaComponentOptions
|
||||||
|
|
||||||
|
在应用中,会有很多层级的嵌套,每一层都可能提供自己的组件和 scope,此组件就是为了层层传递 Schema 所需的 `components` 和 `scope` 的。
|
||||||
|
|
||||||
|
- props
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaComponentOptionsProps {
|
||||||
|
scope?: any;
|
||||||
|
components?: SchemaReactComponents;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
/**
|
||||||
|
* defaultShowCode: true
|
||||||
|
*/
|
||||||
|
import { SchemaComponentProvider, useSchemaComponentContext, SchemaComponent, SchemaComponentOptions } from '@nocobase/client';
|
||||||
|
const World = () => {
|
||||||
|
return <div>world</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Hello = ({ children }) => {
|
||||||
|
return <div>
|
||||||
|
<div>hello</div>
|
||||||
|
<SchemaComponentOptions components={{ World }}>{ children }</SchemaComponentOptions>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'hello',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
properties: {
|
||||||
|
world: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'World',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return <SchemaComponentProvider components={{ Hello }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Root;
|
||||||
|
```
|
@ -0,0 +1,186 @@
|
|||||||
|
# SchemaInitializerManager
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
### schemaInitializerManager.add()
|
||||||
|
|
||||||
|
添加 SchemaInitializer 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
add<P1 = any, P2 = any>(...schemaInitializerList: SchemaInitializer<P1, P2>[]): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add block',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo',
|
||||||
|
type: 'item',
|
||||||
|
title: 'Demo'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializerManager.get()
|
||||||
|
|
||||||
|
获取一个 SchemaInitializer 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
get<P1 = ButtonProps, P2 = {}>(name: string): SchemaInitializer<P1, P2> | undefined
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const myInitializer = this.app.schemaInitializerManager.get('MyInitializer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializerManager.getAll()
|
||||||
|
|
||||||
|
获取所有的 SchemaInitializer 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
getAll(): Record<string, SchemaInitializer<any, any>>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const list = this.app.schemaInitializerManager.getAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### app.schemaInitializerManager.has()
|
||||||
|
|
||||||
|
判断是否有存在某个 SchemaInitializer 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
has(name: string): boolean
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const hasMyInitializer = this.app.schemaInitializerManager.has('MyInitializer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializerManager.remove()
|
||||||
|
|
||||||
|
移除 SchemaInitializer 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
remove(name: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.remove('MyInitializer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializerManager.addItem()
|
||||||
|
|
||||||
|
添加 SchemaInitializer 实例的 Item 项,其和直接 schemaInitializer.add() 方法的区别是,可以确保在实例存在时才会添加。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
addItem(schemaInitializerName: string, itemName: string, data: Omit<SchemaInitializerItemType, 'name'>): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 方式1:先获取,再添加子项,需要确保已注册
|
||||||
|
const myInitializer = this.app.schemaInitializerManager.get('MyInitializer');
|
||||||
|
if (myInitializer) {
|
||||||
|
myInitializer.add('b', { type: 'item', title: 'B' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式2:通过 addItem,内部确保在 MyInitializer 注册时才会添加
|
||||||
|
this.app.schemaInitializerManager.addItem('MyInitializer', 'b', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'B'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializerManager.removeItem()
|
||||||
|
|
||||||
|
移除 实例的 Item 项,其和直接 schemaInitializer.remove() 方法的区别是,可以确保在实例存在时才会移除。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializerManager {
|
||||||
|
removeItem(schemaInitializerName: string, itemName: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 方式1:先获取,再删除子项,需要确保已注册
|
||||||
|
const myInitializer = this.app.schemaInitializerManager.get('MyInitializer');
|
||||||
|
if (myInitializer) {
|
||||||
|
myInitializer.remove('a')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式2:通过 addItem,内部确保在 MyInitializer 注册时才会移除
|
||||||
|
this.app.schemaInitializerManager.remove('MyInitializer', 'a')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,704 @@
|
|||||||
|
# SchemaInitializer
|
||||||
|
|
||||||
|
## new SchemaInitializer(options)
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaInitializerOptions<P1 = ButtonProps, P2 = {}> {
|
||||||
|
Component?: ComponentType<P1>;
|
||||||
|
componentProps?: P1;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
title?: string;
|
||||||
|
icon?: ReactNode;
|
||||||
|
|
||||||
|
items?: SchemaInitializerItemType[];
|
||||||
|
ItemsComponent?: ComponentType<P2>;
|
||||||
|
itemsComponentProps?: P2;
|
||||||
|
itemsComponentStyle?: React.CSSProperties;
|
||||||
|
|
||||||
|
insertPosition?: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
|
||||||
|
designable?: boolean;
|
||||||
|
wrap?: (s: ISchema) => ISchema;
|
||||||
|
onSuccess?: (data: any) => void;
|
||||||
|
insert?: InsertType;
|
||||||
|
useInsert?: () => InsertType;
|
||||||
|
|
||||||
|
popover?: boolean;
|
||||||
|
popoverProps?: PopoverProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SchemaInitializer<P1 = ButtonProps, P2 = {}> {
|
||||||
|
constructor(options: SchemaInitializerOptions<P1, P2> & { name: string }): SchemaInitializer<P1, P2>;
|
||||||
|
add(name: string, item: Omit<SchemaInitializerItemType, 'name'>): void
|
||||||
|
get(nestedName: string): SchemaInitializerItemType | undefined
|
||||||
|
remove(nestedName: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 详细解释
|
||||||
|
|
||||||
|
![](../static/KTUWb69kioUg8bxYTAMc2ReDnRg.png)
|
||||||
|
|
||||||
|
- name:唯一标识,必填
|
||||||
|
- Component 相关
|
||||||
|
|
||||||
|
- Component:触发组件,默认是 `Button` 组件
|
||||||
|
- componentProps: 组件属性,默认是 `ButtonProps`
|
||||||
|
- title: 按钮的文本
|
||||||
|
- icon:按钮的 icon 属性
|
||||||
|
- style:组件的样式
|
||||||
|
- Items 相关
|
||||||
|
|
||||||
|
- items:列表项配置
|
||||||
|
- ItemsComponent:默认是渲染成一个列表的形式,可通过此参数自定义 items
|
||||||
|
- itemsComponentProps:`ItemsComponent` 的属性
|
||||||
|
- itemsComponentStyle:`ItemsComponent` 的样式
|
||||||
|
- popover 组件相关
|
||||||
|
|
||||||
|
- popover:是否使用 popover,默认为 `true`
|
||||||
|
- popoverProps:popover 的属性
|
||||||
|
- Schema 操作相关
|
||||||
|
|
||||||
|
- insertPosition:插入位置,参考:[useDesignable()](/core/ui-schema/designable#usedesignable)
|
||||||
|
- designable:是否显示设计模式,参考:[useDesignable()](/core/ui-schema/designable#usedesignable)
|
||||||
|
- wrap:对 Schema 的二次处理,参考:[useDesignable()](/core/ui-schema/designable#usedesignable)
|
||||||
|
- onSuccess:Schema 更新到服务端后的回调,参考:[useDesignable()](/core/ui-schema/designable#usedesignable)
|
||||||
|
- insert:自定义 Schema 插入逻辑,默认为 [useDesignable()](/core/ui-schema/designable#usedesignable) 的 `insertAdjacent`
|
||||||
|
- useInsert:当自定义插入 Schema 的逻辑需要用到 Hooks 时,可以使用此参数
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
#### 基础用法
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add Block',
|
||||||
|
// 插入位置
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-basic.tsx"></code>
|
||||||
|
|
||||||
|
#### 定制化 `Component`
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
Component: (props) => (
|
||||||
|
<Avatar style={{ cursor: 'pointer' }} {...props}>
|
||||||
|
C
|
||||||
|
</Avatar>
|
||||||
|
),
|
||||||
|
componentProps: {
|
||||||
|
size: 'large',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-component.tsx"></code>
|
||||||
|
|
||||||
|
#### 不使用 Popover
|
||||||
|
|
||||||
|
关于 `useDesignable()` 的说明请参考 [useDesignable](/core/ui-schema/designable#usedesignable)。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
title: Math.random(),
|
||||||
|
'x-component': 'Hello',
|
||||||
|
};
|
||||||
|
const MyInitializerComponent = () => {
|
||||||
|
const { insertBeforeEnd } = useDesignable();
|
||||||
|
return <Button onClick={() => insertBeforeEnd(schema)}>Add block</Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Add block',
|
||||||
|
popover: false,
|
||||||
|
Component: MyInitializerComponent,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-popover.tsx"></code>
|
||||||
|
|
||||||
|
|
||||||
|
#### 定制化 Items
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const CustomListGridMenu: FC<SchemaInitializerItemsProps<ButtonProps, ListProps<any>>> = (props) => {
|
||||||
|
const { items, options, ...others } = props;
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
{...others}
|
||||||
|
style={{ marginTop: 20 }}
|
||||||
|
dataSource={items}
|
||||||
|
grid={{ gutter: 16, column: 2 }}
|
||||||
|
renderItem={(item) => (
|
||||||
|
<List.Item style={{ minWidth: 100, textAlign: 'center' }}>
|
||||||
|
<SchemaInitializerChild {...item} />
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
></List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
ItemsComponent: CustomListGridMenu,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
Component: Demo,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-items.tsx"></code>
|
||||||
|
|
||||||
|
## options.items 配置详解
|
||||||
|
|
||||||
|
### 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaInitializerComponentCommonProps {
|
||||||
|
title?: string;
|
||||||
|
schema?: ISchema;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SchemaInitializerItemBaseType<T = {}> extends SchemaInitializerComponentCommonProps {
|
||||||
|
name: string;
|
||||||
|
sort?: number;
|
||||||
|
type?: string;
|
||||||
|
Component?: string | ComponentType<T>;
|
||||||
|
componentProps?: Omit<T, 'children'>;
|
||||||
|
useComponentProps?: () => Omit<T, 'children'>;
|
||||||
|
useVisible?: () => boolean;
|
||||||
|
children?: SchemaInitializerItemType[];
|
||||||
|
useChildren?: () => SchemaInitializerItemType[];
|
||||||
|
[index: string]: any;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 两种定义方式:`Component` 和 `type`
|
||||||
|
|
||||||
|
|
||||||
|
- 通过 `Component` 定义
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
// 最终渲染 `SchemaInitializerItem`
|
||||||
|
return <SchemaInitializerItem title='Demo' />
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
Component: Demo, // 通过 Component 定义
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- 通过 `type` 定义
|
||||||
|
|
||||||
|
NocoBase 内置了一些常用的 `type`,例如 `type: 'item'`,相当于 `Component: SchemaInitializerItem`。
|
||||||
|
|
||||||
|
更多内置类型,请参考:[内置组件和类型](/core/ui-schema/schema-initializer#%E5%86%85%E7%BD%AE%E7%BB%84%E4%BB%B6%E5%92%8C%E7%B1%BB%E5%9E%8B)
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'item',
|
||||||
|
title: 'Demo'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-options-item-define.tsx"></code>
|
||||||
|
|
||||||
|
### `children` 和动态方式 `useChildren`
|
||||||
|
|
||||||
|
对于某些组件而言是有子列表项的,例如 `type: 'itemGroup'`,那么我们使用 children 属性,同时考虑到某些场景下 children 是动态的,需要从 Hooks 里面获取,那么就可以通过 `useChildren` 来定义。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-options-item-children.tsx"></code>
|
||||||
|
|
||||||
|
### 动态显示隐藏 `useVisible`
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-options-item-visible.tsx"></code>
|
||||||
|
|
||||||
|
### 组件属性 `componentProps` 和动态属性 `useComponentProps`
|
||||||
|
|
||||||
|
对于一些通用组件,我们可以通过 `componentProps` 来定义组件属性,同时考虑到某些场景下组件属性是动态的,需要从 Hooks 里面获取,那么就可以通过 `useComponentProps` 来定义。
|
||||||
|
|
||||||
|
当然也可以不使用这两个属性,直接封装成一个组件,然后通过 `Component` 来定义。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-options-item-props.tsx"></code>
|
||||||
|
|
||||||
|
### 公共属性和组件属性
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
{
|
||||||
|
name: 'demo',
|
||||||
|
title: 'Demo',
|
||||||
|
foo: 'bar',
|
||||||
|
Component: Demo,
|
||||||
|
componentProps: {
|
||||||
|
zzz: 'xxx',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
从上面的示例中我么看到,从配置项中获取组件组件所需的数据有两个方式:
|
||||||
|
|
||||||
|
- 组件属性:通过 `componentProps` 来定义,例如 `zzz: 'xxx'`
|
||||||
|
- 公共属性:将属性直接定义在配置项上,例如 `foo: 'bar'`、`name`、`title`
|
||||||
|
|
||||||
|
在获取上
|
||||||
|
|
||||||
|
- `componentProps` 定义的数据会被传递给组件的 `props`
|
||||||
|
- 直接定义在配置项上的数据会则需要通过 [useSchemaInitializerItem()](/core/ui-schema/schema-initializer#useschemainitializeritem) 获取
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = (props) => {
|
||||||
|
console.log(props); // { zzz: 'xxx' }
|
||||||
|
const { foo } = useSchemaInitializerItem(); // { foo: 'bar' }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'item a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
title: 'item a1',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { SchemaInitializer, Application, useSchemaInitializerRender } from '@nocobase/client';
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'item a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
title: 'item a1',
|
||||||
|
type: 'item',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
const { render } = useSchemaInitializerRender('MyInitializer');
|
||||||
|
return render();
|
||||||
|
}
|
||||||
|
const app = new Application({
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
providers: [Root],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializer.add()
|
||||||
|
|
||||||
|
用于新增 Item,另一种添加方式参考 [schemaInitializerManager.addItem()](/core/ui-schema/schema-initializer-manager#schemainitializermanageradditem);
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializer {
|
||||||
|
add(name: string, item: Omit<SchemaInitializerItemType, 'name'>): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 参数说明
|
||||||
|
|
||||||
|
第一个参数是 name,作为唯一标识,用于增删改查,并且 `name` 支持 `.` 用于分割层级。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
myInitializer.add('b', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'item b',
|
||||||
|
})
|
||||||
|
|
||||||
|
myInitializer.add('a.a2', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'item a2',
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { SchemaInitializer, Application, useSchemaInitializerRender } from '@nocobase/client';
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'item a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
title: 'item a1',
|
||||||
|
type: 'item',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
myInitializer.add('b', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'item b',
|
||||||
|
})
|
||||||
|
|
||||||
|
myInitializer.add('a.a2', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'item a2',
|
||||||
|
})
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
const { render } = useSchemaInitializerRender('MyInitializer');
|
||||||
|
return render();
|
||||||
|
}
|
||||||
|
const app = new Application({
|
||||||
|
schemaInitializers: [myInitializer],
|
||||||
|
providers: [Root],
|
||||||
|
designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### schemaInitializer.get()
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializer {
|
||||||
|
get(nestedName: string): SchemaInitializerItemType | undefined
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const itemA = myInitializer.get('a')
|
||||||
|
|
||||||
|
const itemA1 = myInitializer.add('a.a1')
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaInitializer.remove()
|
||||||
|
|
||||||
|
另一种移除方式参考 [schemaInitializerManager.addItem()](/core/ui-schema/schema-initializer-manager#schemainitializermanagerremoveitem);
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaInitializer {
|
||||||
|
remove(nestedName: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
myInitializer.remove('a.a1')
|
||||||
|
|
||||||
|
myInitializer.remove('a')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useSchemaInitializer()
|
||||||
|
|
||||||
|
用于获取 `SchemaInitializer` 上下文内容。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
export type InsertType = (s: ISchema) => void;
|
||||||
|
|
||||||
|
const useSchemaInitializer: () => {
|
||||||
|
insert: InsertType;
|
||||||
|
options: SchemaInitializerOptions<any>;
|
||||||
|
visible?: boolean;
|
||||||
|
setVisible?: (v: boolean) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 参数详解
|
||||||
|
- `insert`:参数是 Schema 对象,用于插入 Schema
|
||||||
|
- `options`:获取 `new SchemaInitializer(options)` 时 options 配置
|
||||||
|
- `visible`:popover 是否显示
|
||||||
|
- `setVisible`:设置 popover 显示状态
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
}
|
||||||
|
const Demo = () => {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert(schema);
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={'Demo'} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### useSchemaInitializerRender()
|
||||||
|
|
||||||
|
用于渲染 `SchemaInitializer`。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
function useSchemaInitializerRender(name: string, options?: SchemaInitializerOptions): {
|
||||||
|
exists: boolean;
|
||||||
|
render: (props?: SchemaInitializerOptions) => React.FunctionComponentElement;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 参数详解
|
||||||
|
|
||||||
|
返回的 `render` 方法可以接收一个参数,用于覆盖 `new SchemaInitializer(options)` 的 `options` 配置。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const filedSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
||||||
|
return <div>
|
||||||
|
<div>{ render() }</div>
|
||||||
|
<div>可以进行参数的二次覆盖:{ render({ style: { color: 'red' } }) }</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-render.tsx"></code>
|
||||||
|
|
||||||
|
### useSchemaInitializerItem()
|
||||||
|
|
||||||
|
用于获取配置项内容的,配置项是指的 `SchemaInitializer` 中的 `items` 中的一项。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const useSchemaInitializerItem: <T = any>() => T
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
title: 'Item A',
|
||||||
|
foo: 'bar',
|
||||||
|
Component: Demo,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 useSchemaInitializerItem() 获取到的是
|
||||||
|
* {
|
||||||
|
* name: 'a',
|
||||||
|
* title: 'Item A',
|
||||||
|
* foo: 'bar',
|
||||||
|
* Component: Demo,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const Demo = () => {
|
||||||
|
const { title, foo } = useSchemaInitializerItem();
|
||||||
|
return <div>{ title } - { foo }</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-hooks-item.tsx"></code>
|
||||||
|
|
||||||
|
## 内置组件和类型
|
||||||
|
|
||||||
|
| type | Component | 效果 |
|
||||||
|
| ----------- | ------------------------------ | ----------------------------------------- |
|
||||||
|
| item | SchemaInitializerItem | 文本|
|
||||||
|
| itemGroup | SchemaInitializerItemGroup | 分组,同 antd `Menu` 组件的 `type: 'group'` |
|
||||||
|
| subMenu | SchemaInitializerSubMenu | 子菜单,同 antd `Menu` 组件的子菜单 |
|
||||||
|
| divider | SchemaInitializerDivider | 分割线,同 antd `Menu` 组件的 `type: 'divider'` |
|
||||||
|
| switch | SchemaInitializerSwitch | 开关 |
|
||||||
|
| actionModal | SchemaInitializerActionModal | 弹窗|
|
||||||
|
|
||||||
|
以下每个示例都提供了 2 种[定义方式](/core/ui-schema/schema-initializer#两种定义方式component-和-type),一种是通过 `Component` 定义,另一种是通过 `type` 定义。
|
||||||
|
|
||||||
|
### `type: 'item'` & `SchemaInitializerItem`
|
||||||
|
|
||||||
|
文本项。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaInitializerItemProps {
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
className?: string;
|
||||||
|
name?: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
title?: React.ReactNode;
|
||||||
|
items?: SchemaInitializerItemType[];
|
||||||
|
onClick?: (args?: any) => any;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
核心参数是 `title`、`icon`、`onClick`、`items`,其中 `onClick` 用于插入 Schema,`items` 用于渲染子列表项。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-components-item.tsx"></code>
|
||||||
|
|
||||||
|
### `type: 'itemGroup'` & SchemaInitializerItemGroup
|
||||||
|
|
||||||
|
分组。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaInitializerItemGroupProps {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
children?: SchemaInitializerOptions['items'];
|
||||||
|
divider?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
核心参数是 `title`、`children`,其中 `children` 用于渲染子列表项,`divider` 用于渲染分割线。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-components-group.tsx"></code>
|
||||||
|
|
||||||
|
### `type: 'switch'` & SchemaInitializerSwitch
|
||||||
|
|
||||||
|
Switch 切换按钮。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaInitializerSwitchItemProps extends SchemaInitializerItemProps {
|
||||||
|
checked?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
核心参数是 `checked`、`onClick`,其中 `onClick` 用于插入或者移除 Schema。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-components-switch.tsx"></code>
|
||||||
|
|
||||||
|
### `type: 'subMenu'` & SchemaInitializerSubMenu
|
||||||
|
|
||||||
|
子菜单。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-components-menu.tsx"></code>
|
||||||
|
|
||||||
|
### `type: 'divider'` & SchemaInitializerDivider
|
||||||
|
|
||||||
|
分割线。
|
||||||
|
|
||||||
|
<code src="./demos/schema-initializer-components-divider.tsx"></code>
|
||||||
|
|
||||||
|
## 渲染组件
|
||||||
|
|
||||||
|
### SchemaInitializerChildren
|
||||||
|
|
||||||
|
用于自定义渲染多个列表项。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
|
||||||
|
const Demo = ({ children }) => {
|
||||||
|
// children: [{ name: 'a1', Component: ItemA1 }, { name: 'a2', type: 'item', title: 'ItemA2' }]
|
||||||
|
return <SchemaInitializerChildren>{ children }</SchemaInitializerChildren>
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
Component: Demo,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
Component: ItemA1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a2',
|
||||||
|
type: 'item',
|
||||||
|
title: 'ItemA2',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### SchemaInitializerChild
|
||||||
|
|
||||||
|
用于自定义渲染单个列表项。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = (props) => {
|
||||||
|
return <SchemaInitializerChild {...props}/>
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,188 @@
|
|||||||
|
# SchemaSettingsManager
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
### schemaSettingsManager.add()
|
||||||
|
|
||||||
|
添加 SchemaSettings 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
add<T = any>(...schemaSettingList: SchemaSetting<T>[]): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const mySchemaSettings = new SchemaSetting({
|
||||||
|
name: 'MySchemaSettings',
|
||||||
|
title: 'Add block',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo',
|
||||||
|
type: 'item',
|
||||||
|
componentProps:{
|
||||||
|
title: 'Demo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaSettingsManager.add(mySchemaSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettingsManager.get()
|
||||||
|
|
||||||
|
获取一个 SchemaSettings 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
get<T = any>(name: string): SchemaSetting<T> | undefined
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const mySchemaSettings = this.app.schemaSettingsManager.get('MySchemaSettings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettingsManager.getAll()
|
||||||
|
|
||||||
|
获取所有的 SchemaSettings 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
getAll(): Record<string, SchemaInitializer<any, any>>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const list = this.app.schemaSettingsManager.getAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### app.schemaSettingsManager.has()
|
||||||
|
|
||||||
|
判断是否有存在某个 SchemaSettings 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
has(name: string): boolean
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
const hasMySchemaSettings = this.app.schemaSettingsManager.has('MySchemaSettings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettingsManager.remove()
|
||||||
|
|
||||||
|
移除 SchemaSettings 实例。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
remove(name: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaSettingsManager.remove('MySchemaSettings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettingsManager.addItem()
|
||||||
|
|
||||||
|
添加 SchemaSettings 实例的 Item 项,其和直接 schemaInitializer.add() 方法的区别是,可以确保在实例存在时才会添加。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
addItem(schemaInitializerName: string, itemName: string, data: Omit<SchemaInitializerItemType, 'name'>): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 方式1:先获取,再添加子项,需要确保已注册
|
||||||
|
const mySchemaSettings = this.app.schemaSettingsManager.get('MySchemaSettings');
|
||||||
|
if (mySchemaSettings) {
|
||||||
|
mySchemaSettings.add('b', { type: 'item', componentProps:{ title: 'B' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式2:通过 addItem,内部确保在 mySchemaSettings 注册时才会添加
|
||||||
|
this.app.schemaSettingsManager.addItem('MySchemaSettings', 'b', {
|
||||||
|
type: 'item',
|
||||||
|
componentProps:{ title: 'B' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettingsManager.removeItem()
|
||||||
|
|
||||||
|
移除 实例的 Item 项,其和直接 schemaInitializer.remove() 方法的区别是,可以确保在实例存在时才会移除。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettingsManager {
|
||||||
|
removeItem(schemaInitializerName: string, itemName: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
// 方式1:先获取,再删除子项,需要确保已注册
|
||||||
|
const mySchemaSettings = this.app.schemaSettingsManager.get('MySchemaSettings');
|
||||||
|
if (mySchemaSettings) {
|
||||||
|
mySchemaSettings.remove('a')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式2:通过 addItem,内部确保在 mySchemaSettings 注册时才会移除
|
||||||
|
this.app.schemaSettingsManager.remove('MySchemaSettings', 'a')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,467 @@
|
|||||||
|
# SchemaSettings
|
||||||
|
|
||||||
|
## new SchemaSettings(options)
|
||||||
|
|
||||||
|
创建一个 SchemaSettings 实例。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaSettingsOptions<T = {}> {
|
||||||
|
name: string;
|
||||||
|
Component?: ComponentType<T>;
|
||||||
|
componentProps?: T;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
|
||||||
|
items: SchemaSettingsItemType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SchemaSettings<T = {}>{
|
||||||
|
constructor(options: SchemaSettingsOptions<T>): SchemaSettings<T>;
|
||||||
|
add(name: string, item: Omit<SchemaSettingsItemType, 'name'>): void
|
||||||
|
get(nestedName: string): SchemaSettingsItemType | undefined
|
||||||
|
remove(nestedName: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 详细解释
|
||||||
|
|
||||||
|
![](../static/VfPGbhWo9os0qTxcITkcHNfin4g.png)
|
||||||
|
|
||||||
|
- name:唯一标识,必填
|
||||||
|
- Component 相关
|
||||||
|
|
||||||
|
- Component:触发组件,默认是 `<MenuOutlined />` 组件
|
||||||
|
- componentProps: 组件属性
|
||||||
|
- style:组件的样式
|
||||||
|
- items:列表项配置
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
#### 基础用法
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const mySchemaSettings = new SchemaSettings({
|
||||||
|
name: 'MySchemaSettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo1', // 唯一标识
|
||||||
|
type: 'item', // 内置类型
|
||||||
|
componentProps: {
|
||||||
|
title: 'DEMO1',
|
||||||
|
onClick() {
|
||||||
|
alert('DEMO1');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'demo2',
|
||||||
|
Component: () => <SchemaSettings.Item title="DEMO2" onClick={() => alert('DEMO2')} />, // 直接使用 Component 组件
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 定制化 `Component`
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const mySchemaSettings = new SchemaSettings({
|
||||||
|
name: 'MySchemaSettings',
|
||||||
|
Component: Button, // 自定义组件
|
||||||
|
componentProps: {
|
||||||
|
type: 'primary',
|
||||||
|
children: '自定义按钮',
|
||||||
|
},
|
||||||
|
// Component: (props) => <Button type='primary' {...props}>自定义按钮</Button>, // 等同于上面效果
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo1',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'DEMO',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## options.items 配置详解
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaSettingsItemCommon<T = {}> {
|
||||||
|
name: string;
|
||||||
|
sort?: number;
|
||||||
|
type?: string;
|
||||||
|
Component: string | ComponentType<T>;
|
||||||
|
useVisible?: () => boolean;
|
||||||
|
children?: SchemaSettingsItemType[];
|
||||||
|
useChildren?: () => SchemaSettingsItemType[];
|
||||||
|
checkChildrenLength?: boolean;
|
||||||
|
componentProps?: Omit<T, 'children'>;
|
||||||
|
useComponentProps?: () => Omit<T, 'children'>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 两种定义方式:`Component` 和 `type`
|
||||||
|
|
||||||
|
|
||||||
|
- 通过 `Component` 定义
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
// 最终渲染 `SchemaSettingsItem`
|
||||||
|
return <SchemaSettingsItem title='Demo' />
|
||||||
|
}
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
Component: Demo, // 通过 Component 定义
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- 通过 `type` 定义
|
||||||
|
|
||||||
|
NocoBase 内置了一些常用的 `type`,例如 `type: 'item'`,相当于 `Component: SchemaSettingsItem`。
|
||||||
|
|
||||||
|
更多内置类型,请参考:[内置组件和类型](/core/ui-schema/schema-settings#%E5%86%85%E7%BD%AE%E7%BB%84%E4%BB%B6%E5%92%8C%E7%B1%BB%E5%9E%8B)
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'item',
|
||||||
|
componentProps: {
|
||||||
|
title: 'Demo',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-options-item-define.tsx"></code>
|
||||||
|
|
||||||
|
### `children` 和动态方式 `useChildren`
|
||||||
|
|
||||||
|
对于某些组件而言是有子列表项的,例如 `type: 'itemGroup'`,那么我们使用 children 属性,同时考虑到某些场景下 children 是动态的,需要从 Hooks 里面获取,那么就可以通过 `useChildren` 来定义。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-options-item-children.tsx"></code>
|
||||||
|
|
||||||
|
### 动态显示隐藏 `useVisible`
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-options-item-visible.tsx"></code>
|
||||||
|
|
||||||
|
### 组件属性 `componentProps` 和动态属性 `useComponentProps`
|
||||||
|
|
||||||
|
对于一些通用组件,我们可以通过 `componentProps` 来定义组件属性,同时考虑到某些场景下组件属性是动态的,需要从 Hooks 里面获取,那么就可以通过 `useComponentProps` 来定义。
|
||||||
|
|
||||||
|
当然也可以不使用这两个属性,直接封装成一个组件,然后通过 `Component` 来定义。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-options-item-props.tsx"></code>
|
||||||
|
|
||||||
|
## 实例方法
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const mySchemaSettings = new SchemaSettings({
|
||||||
|
name: 'MySchemaSettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'a',
|
||||||
|
type: 'itemGroup',
|
||||||
|
componentProps: {
|
||||||
|
title: 'item a'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'a1',
|
||||||
|
title: 'item a1',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettings.add()
|
||||||
|
|
||||||
|
用于新增 Item。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettings {
|
||||||
|
add(name: string, item: Omit<SchemaSettingsItemType, 'name'>): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 参数说明
|
||||||
|
|
||||||
|
第一个参数是 name,作为唯一标识,用于增删改查,并且 `name` 支持 `.` 用于分割层级。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
mySchemaSetting.add('b', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'item b',
|
||||||
|
})
|
||||||
|
|
||||||
|
mySchemaSetting.add('a.a2', {
|
||||||
|
type: 'item',
|
||||||
|
title: 'item a2',
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettings.get()
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettings {
|
||||||
|
get(nestedName: string): SchemaSettingsItemType| undefined
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const itemA = mySchemaSetting.get('a')
|
||||||
|
|
||||||
|
const itemA1 = mySchemaSetting.add('a.a1')
|
||||||
|
```
|
||||||
|
|
||||||
|
### schemaSettings.remove()
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
class SchemaSettings {
|
||||||
|
remove(nestedName: string): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
mySchemaSetting.remove('a.a1')
|
||||||
|
|
||||||
|
mySchemaSetting.remove('a')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useSchemaSettingsRender()
|
||||||
|
|
||||||
|
用于渲染 SchemaInitializer。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
function useSchemaSettingsRender(name: string, options?: SchemaSettingsOptions): {
|
||||||
|
exists: boolean;
|
||||||
|
render: (options?: SchemaSettingsRenderOptions) => React.ReactElement;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const Demo = () => {
|
||||||
|
const filedSchema = useFieldSchema();
|
||||||
|
const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], fieldSchema['x-settings-props'])
|
||||||
|
return <div>
|
||||||
|
<div>{ render() }</div>
|
||||||
|
<div>可以进行参数的二次覆盖:{ render({ style: { color: 'red' } }) }</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-render.tsx"></code>
|
||||||
|
|
||||||
|
### useSchemaSettings()
|
||||||
|
|
||||||
|
获取 schemaSetting 上下文数据。
|
||||||
|
|
||||||
|
上下文数据包含了 `schemaSetting` 实例化时的 `options` 以及调用 `useSchemaSettingsRender()` 时传入的 `options`。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface UseSchemaSettingsResult<T> extends SchemaSettingsOptions<T> {
|
||||||
|
dn?: Designable;
|
||||||
|
field?: GeneralField;
|
||||||
|
fieldSchema?: Schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useSchemaSettings(): UseSchemaSettingsResult;
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const { dn } = useSchemaSettings();
|
||||||
|
```
|
||||||
|
|
||||||
|
### useSchemaSettingsItem()
|
||||||
|
|
||||||
|
用于获取一个 item 的数据。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
export type SchemaSettingsItemType<T = {}> = {
|
||||||
|
name: string;
|
||||||
|
type?: string;
|
||||||
|
sort?: number;
|
||||||
|
Component?: string | ComponentType<T>;
|
||||||
|
componentProps?: T;
|
||||||
|
useComponentProps?: () => T;
|
||||||
|
useVisible?: () => boolean;
|
||||||
|
children?: SchemaSettingsItemType[];
|
||||||
|
[index]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useSchemaSettingsItem(): SchemaSettingsItemType;
|
||||||
|
```
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const { name } = useSchemaSettingsItem();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 内置组件和类型
|
||||||
|
|
||||||
|
| type | Component | 效果 |
|
||||||
|
| ----------- | ------------------------------ | ----------------------------------------- |
|
||||||
|
| item | SchemaSettingsItem | 文本 |
|
||||||
|
| itemGroup | SchemaSettingsItemGroup | 分组,同 Menu 组件的 `type: 'itemGroup'` |
|
||||||
|
| subMenu | SchemaSettingsSubMenu | 子菜单,同 Menu 组件的子菜单 |
|
||||||
|
| divider | SchemaSettingsDivider | 分割线,同 Menu 组件的 `type: 'divider'` |
|
||||||
|
| remove | SchemaSettingsRemove | 删除,用于删除一个区块 |
|
||||||
|
| select | SchemaSettingsSelectItem | 下拉选择 |
|
||||||
|
| cascader | SchemaSettingsCascaderItem | 级联选择 |
|
||||||
|
| switch | SchemaSettingsSwitchItem | 开关 |
|
||||||
|
| popup | SchemaSettingsPopupItem | 弹出层 |
|
||||||
|
| actionModal | SchemaSettingsActionModalItem | 操作弹窗 |
|
||||||
|
| modal | SchemaSettingsModalItem | 弹窗 |
|
||||||
|
|
||||||
|
### SchemaSettingsItem
|
||||||
|
|
||||||
|
文本,对应的 `type` 为 `item`。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaSettingsItemProps extends Omit<MenuItemProps, 'title'> {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
核心参数为 `title` 和 `onClick`,可以在 `onClick` 中修改 schema。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-item.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsItemGroup
|
||||||
|
|
||||||
|
分组,对应的 `type` 为 `itemGroup`。
|
||||||
|
|
||||||
|
核心参数是 `title`。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-group.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsSubMenu
|
||||||
|
|
||||||
|
子菜单,对应的 `type` 为 `subMenu`。
|
||||||
|
|
||||||
|
核心参数是 `title`。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-sub-menu.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsDivider
|
||||||
|
|
||||||
|
分割线,对应的 `type` 为 `divider`。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-divider.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsRemove
|
||||||
|
|
||||||
|
删除,对应的 `type` 为 `remove`。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaSettingsRemoveProps {
|
||||||
|
confirm?: ModalFuncProps;
|
||||||
|
removeParentsIfNoChildren?: boolean;
|
||||||
|
breakRemoveOn?: ISchema | ((s: ISchema) => boolean);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `confirm`:删除前的确认弹窗
|
||||||
|
- `removeParentsIfNoChildren`:如果删除后没有子节点了,是否删除父节点
|
||||||
|
- `breakRemoveOn`:如果删除的节点满足条件,是否中断删除
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-remove.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsSelectItem
|
||||||
|
|
||||||
|
选择器,对应的 `type` 为 `select`。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-select.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsCascaderItem
|
||||||
|
|
||||||
|
级联选择,对应的 `type` 为 `cascader`。
|
||||||
|
|
||||||
|
### SchemaSettingsSwitchItem
|
||||||
|
|
||||||
|
开关,对应的 `type` 为 `switch`。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-switch.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsModalItem
|
||||||
|
|
||||||
|
弹窗,对应的 `type` 为 `modal`。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
export interface SchemaSettingsModalItemProps {
|
||||||
|
title: string;
|
||||||
|
onSubmit: (values: any) => void;
|
||||||
|
initialValues?: any;
|
||||||
|
schema?: ISchema | (() => ISchema);
|
||||||
|
modalTip?: string;
|
||||||
|
components?: any;
|
||||||
|
hidden?: boolean;
|
||||||
|
scope?: any;
|
||||||
|
effects?: any;
|
||||||
|
width?: string | number;
|
||||||
|
children?: ReactNode;
|
||||||
|
asyncGetInitialValues?: () => Promise<any>;
|
||||||
|
eventKey?: string;
|
||||||
|
hide?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
我们可以通过 `schema` 参数来定义弹窗的表单,然后在 `onSubmit` 中获取表单的值,然后修改当前 schema 节点。
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-modal.tsx"></code>
|
||||||
|
|
||||||
|
### SchemaSettingsActionModalItem
|
||||||
|
|
||||||
|
操作弹窗,对应的 `type` 为 `actionModal`。
|
||||||
|
|
||||||
|
其和 `modal` 的区别是,`SchemaSettingsModalItem` 弹窗会丢失上下文,而 `SchemaSettingsActionModalItem` 会保留上下文,简单场景下可以使用 `SchemaSettingsModalItem`,复杂场景下可以使用 `SchemaSettingsActionModalItem`。
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
export interface SchemaSettingsActionModalItemProps extends SchemaSettingsModalItemProps, Omit<SchemaSettingsItemProps, 'onSubmit' | 'onClick'> {
|
||||||
|
uid?: string;
|
||||||
|
initialSchema?: ISchema;
|
||||||
|
schema?: ISchema;
|
||||||
|
beforeOpen?: () => void;
|
||||||
|
maskClosable?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<code src="./demos/schema-settings-components-action-modal.tsx"></code>
|
427
packages/core/client/docs/en-US/core/ui-schema/schema-toolbar.md
Normal file
427
packages/core/client/docs/en-US/core/ui-schema/schema-toolbar.md
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
# SchemaToolbar
|
||||||
|
|
||||||
|
![SchemaToolbar](../static/designer.png)
|
||||||
|
|
||||||
|
## 组件
|
||||||
|
|
||||||
|
### SchemaToolbar 组件
|
||||||
|
|
||||||
|
此为默认的 Toolbar 组件,其内部会自动根据 Schema 的 `x-settings`、`x-initializer` 渲染 `SchemaSettings`、`SchemaInitializer` 和 `Drag` 组件。
|
||||||
|
|
||||||
|
Toolbar 具体的渲染规则为:当有 `x-toolbar` 会渲染对应的组件;当无 `x-toolbar` 但是有 `x-settings`、`x-initializer` 会渲染默认的 `SchemaToolbar` 组件。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
interface SchemaToolbarProps {
|
||||||
|
title?: string;
|
||||||
|
draggable?: boolean;
|
||||||
|
initializer?: string | false;
|
||||||
|
settings?: string | false;
|
||||||
|
/**
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
showBorder?: boolean;
|
||||||
|
showBackground?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
- `title`:左上角的标题
|
||||||
|
- `draggable`:是否可以拖拽,默认为 `true`
|
||||||
|
- `initializer`:`SchemaInitializer` 的默认值,当 schema 里没有 `x-initializer` 时,会使用此值;当为 `false` 时,不会渲染 `SchemaInitializer`
|
||||||
|
- `settings`:`SchemaSettings` 的默认值,当 schema 里没有 `x-settings` 时,会使用此值;当为 `false` 时,不会渲染 `SchemaSettings`
|
||||||
|
- `showBorder`:边框是否变为橘色
|
||||||
|
- `showBackground`:背景是否变为橘色
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
未指定 `x-toolbar` 时会渲染默认的 `SchemaToolbar` 这个组件。
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
CardItem,
|
||||||
|
Grid,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
SchemaSettings,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
wrap: Grid.wrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo1',
|
||||||
|
title: 'Demo1',
|
||||||
|
Component: () => {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello = () => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
return <h1>Hello, world! {schema.name}</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hello1 = Grid.wrap({
|
||||||
|
type: 'void',
|
||||||
|
// 仅指定了 `x-settings` 但是没有 `x-toolbar`,会使用默认的 `SchemaToolbar` 组件
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
|
||||||
|
const HelloPage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'root',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginHello extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({ Grid, CardItem, Hello });
|
||||||
|
this.app.schemaSettingsManager.add(mySettings);
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.router.add('hello', {
|
||||||
|
path: '/',
|
||||||
|
Component: HelloPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
plugins: [PluginHello],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
自定义 Toolbar 组件。
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
CardItem,
|
||||||
|
Grid,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
SchemaSettings,
|
||||||
|
SchemaToolbar,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const MyToolbar = () => {
|
||||||
|
return <SchemaToolbar showBackground title='Test' />
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
wrap: Grid.wrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo1',
|
||||||
|
title: 'Demo1',
|
||||||
|
Component: () => {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
// 调用插入功能
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
// 使用自定义的 Toolbar 组件
|
||||||
|
'x-toolbar': 'MyToolbar',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello = () => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
return <h1>Hello, world! {schema.name}</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hello1 = Grid.wrap({
|
||||||
|
type: 'void',
|
||||||
|
// 使用自定义的 Toolbar 组件
|
||||||
|
'x-toolbar': 'MyToolbar',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'CardItem',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
|
||||||
|
const HelloPage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'root',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginHello extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({ Grid, CardItem, Hello, MyToolbar });
|
||||||
|
this.app.schemaSettingsManager.add(mySettings);
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.router.add('hello', {
|
||||||
|
path: '/',
|
||||||
|
Component: HelloPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
plugins: [PluginHello],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
### useSchemaToolbarRender()
|
||||||
|
|
||||||
|
用于渲染 `SchemaToolbar`。
|
||||||
|
|
||||||
|
- 类型
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const useSchemaToolbarRender: (fieldSchema: ISchema) => {
|
||||||
|
render(props?: SchemaToolbarProps): React.JSX.Element;
|
||||||
|
exists: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 详细解释
|
||||||
|
|
||||||
|
前面示例中 `'x-decorator': 'CardItem'` 中组件 `CardItem` 里面就调用了 `useSchemaToolbarRender()` 进行渲染。内置的组件还有:`BlockItem`、`CardItem`、`Action`、`FormItem`。
|
||||||
|
|
||||||
|
`render()` 支持二次覆盖组件属性。
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const MyDecorator = () => {
|
||||||
|
const filedSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaToolbarRender(filedSchema); // 从 Schema 中读取 Toolbar 组件
|
||||||
|
|
||||||
|
return <Card>{ render() }</Card>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Card } from 'antd';
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
CardItem,
|
||||||
|
Grid,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
SchemaSettings,
|
||||||
|
SchemaToolbar,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
useSchemaToolbarRender
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const mySettings = new SchemaSettings({
|
||||||
|
name: 'mySettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const MyToolbar = (props) => {
|
||||||
|
return <SchemaToolbar showBackground title='Test' {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义包装器
|
||||||
|
const MyDecorator = ({children}) => {
|
||||||
|
const filedSchema = useFieldSchema();
|
||||||
|
// 使用 `useSchemaToolbarRender()` 获取并渲染内容
|
||||||
|
const { render } = useSchemaToolbarRender(filedSchema);
|
||||||
|
return <Card style={{ marginBottom: 10 }}>{ render({ draggable: false }) }{children}</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'MyInitializer',
|
||||||
|
title: 'Button Text',
|
||||||
|
wrap: Grid.wrap,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'demo1',
|
||||||
|
title: 'Demo1',
|
||||||
|
Component: () => {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
// 调用插入功能
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
// 使用自定义的 Toolbar 组件
|
||||||
|
'x-toolbar': 'MyToolbar',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'MyDecorator',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={itemConfig.title} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hello = () => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
return <h1>Hello, world! {schema.name}</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hello1 = Grid.wrap({
|
||||||
|
type: 'void',
|
||||||
|
// 使用自定义的 Toolbar 组件
|
||||||
|
'x-toolbar': 'MyToolbar',
|
||||||
|
'x-settings': 'mySettings',
|
||||||
|
'x-decorator': 'MyDecorator',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
|
||||||
|
const HelloPage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: 'root',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'MyInitializer',
|
||||||
|
properties: {
|
||||||
|
hello1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginHello extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({ Grid, CardItem, Hello, MyToolbar, MyDecorator });
|
||||||
|
this.app.schemaSettingsManager.add(mySettings);
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.router.add('hello', {
|
||||||
|
path: '/',
|
||||||
|
Component: HelloPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
plugins: [PluginHello],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
||||||
|
```
|
7
packages/core/client/docs/en-US/core/utils.md
Normal file
7
packages/core/client/docs/en-US/core/utils.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Utils
|
||||||
|
|
||||||
|
## Function
|
||||||
|
|
||||||
|
### tval
|
||||||
|
|
||||||
|
用于输出多语言的字符串。
|
3
packages/core/client/docs/en-US/index.md
Normal file
3
packages/core/client/docs/en-US/index.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Index
|
||||||
|
|
||||||
|
<Navigate replace to="/core/application/application"></Navigate>
|
82
packages/core/client/docs/en-US/ui-schema/actions/add-new.md
Normal file
82
packages/core/client/docs/en-US/ui-schema/actions/add-new.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Add new
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-action": "create",
|
||||||
|
"x-acl-action": "create",
|
||||||
|
"title": "{{t('Add new')}}",
|
||||||
|
"x-designer": "Action.Designer",
|
||||||
|
"x-component": "Action",
|
||||||
|
"x-decorator": "ACLActionProvider",
|
||||||
|
"x-component-props": {
|
||||||
|
"openMode": "drawer",
|
||||||
|
"type": "primary",
|
||||||
|
"component": "CreateRecordAction",
|
||||||
|
"icon": "PlusOutlined"
|
||||||
|
},
|
||||||
|
"x-align": "right",
|
||||||
|
"x-acl-action-props": {
|
||||||
|
"skipScopeCheck": true
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"Add record\") }}",
|
||||||
|
"x-component": "Action.Container",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializersForCreateFormBlock",
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t(\"Add new\")}}",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "CreateFormBlockInitializers",
|
||||||
|
"x-uid": "psh2rj6pkab",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "3i7grk0aytf",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "75s24n1nlkh",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "wg1f4xyx7vp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "2hwye7mt2th",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1 @@
|
|||||||
|
# Add record(任意表)
|
@ -0,0 +1 @@
|
|||||||
|
# 批量编辑
|
@ -0,0 +1 @@
|
|||||||
|
# 批量更新
|
@ -0,0 +1,88 @@
|
|||||||
|
# 自定义请求
|
||||||
|
|
||||||
|
<Alert type="warning">
|
||||||
|
x-component 不统一,x-action 也不统一,有三种风格的 schema
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 表单的自定义请求
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"title": "{{ t(\"Custom request\") }}",
|
||||||
|
"x-component": "CustomRequestAction",
|
||||||
|
"x-action": "customize:form:request",
|
||||||
|
"x-designer": "CustomRequestAction.Designer",
|
||||||
|
"x-decorator": "CustomRequestAction.Decorator",
|
||||||
|
"x-action-settings": {
|
||||||
|
"onSuccess": {
|
||||||
|
"manualClose": false,
|
||||||
|
"redirecting": false,
|
||||||
|
"successMessage": "{{t(\"Request success\")}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-uid": "d1rfalvivfo",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 表格列操作的自定义请求
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"title": "{{ t(\"Custom request\") }}",
|
||||||
|
"x-component": "Action.Link",
|
||||||
|
"x-action": "customize:table:request",
|
||||||
|
"x-designer": "Action.Designer",
|
||||||
|
"x-action-settings": {
|
||||||
|
"requestSettings": {},
|
||||||
|
"onSuccess": {
|
||||||
|
"manualClose": false,
|
||||||
|
"redirecting": false,
|
||||||
|
"successMessage": "{{t(\"Request success\")}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useCustomizeRequestActionProps }}"
|
||||||
|
},
|
||||||
|
"x-designer-props": {
|
||||||
|
"linkageAction": true
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-uid": "edn0y4fq7xb",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 详情的自定义请求
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"title": "{{ t(\"Custom request\") }}",
|
||||||
|
"x-component": "CustomRequestAction",
|
||||||
|
"x-action": "customize:form:request",
|
||||||
|
"x-designer": "CustomRequestAction.Designer",
|
||||||
|
"x-decorator": "CustomRequestAction.Decorator",
|
||||||
|
"x-action-settings": {
|
||||||
|
"onSuccess": {
|
||||||
|
"manualClose": false,
|
||||||
|
"redirecting": false,
|
||||||
|
"successMessage": "{{t(\"Request success\")}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-uid": "glc5zkr0ke3",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1 @@
|
|||||||
|
# 删除
|
@ -0,0 +1 @@
|
|||||||
|
# 复制
|
@ -0,0 +1 @@
|
|||||||
|
# 编辑
|
@ -0,0 +1 @@
|
|||||||
|
# 导出
|
@ -0,0 +1 @@
|
|||||||
|
# 筛选
|
@ -0,0 +1 @@
|
|||||||
|
# 导入
|
51
packages/core/client/docs/en-US/ui-schema/actions/index.md
Normal file
51
packages/core/client/docs/en-US/ui-schema/actions/index.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# 概览
|
||||||
|
|
||||||
|
## UI Schema 协议
|
||||||
|
|
||||||
|
- `x-action`
|
||||||
|
- `x-align`
|
||||||
|
- `x-acl-action`
|
||||||
|
- `x-acl-action-props`
|
||||||
|
- `x-action-settings`
|
||||||
|
|
||||||
|
## 常规操作
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"Submit\") }}",
|
||||||
|
"x-component": "Action",
|
||||||
|
"x-component-props": {
|
||||||
|
"type": "primary",
|
||||||
|
"htmlType": "submit",
|
||||||
|
"useProps": "{{ useCreateActionProps }}"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 弹窗操作
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t('Open drawer')}}",
|
||||||
|
"x-component": "Action",
|
||||||
|
"x-component-props": {
|
||||||
|
"openMode": "drawer",
|
||||||
|
"type": "primary",
|
||||||
|
"icon": "PlusOutlined"
|
||||||
|
},
|
||||||
|
"x-align": "right",
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Container",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
更多示例查看 [Action](/apis/action)
|
@ -0,0 +1 @@
|
|||||||
|
# 打印
|
@ -0,0 +1 @@
|
|||||||
|
# 刷新
|
@ -0,0 +1 @@
|
|||||||
|
# 保存数据
|
@ -0,0 +1 @@
|
|||||||
|
# 提交至工作流
|
28
packages/core/client/docs/en-US/ui-schema/actions/submit.md
Normal file
28
packages/core/client/docs/en-US/ui-schema/actions/submit.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 提交
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 新建数据的提交
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"title": "{{ t(\"Submit\") }}",
|
||||||
|
"x-action": "submit",
|
||||||
|
"x-component": "Action",
|
||||||
|
"x-designer": "Action.Designer",
|
||||||
|
"x-component-props": {
|
||||||
|
"type": "primary",
|
||||||
|
"htmlType": "submit",
|
||||||
|
"useProps": "{{ useCreateActionProps }}"
|
||||||
|
},
|
||||||
|
"x-action-settings": {
|
||||||
|
"triggerWorkflows": []
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-uid": "aaxbm1i5xxd",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1 @@
|
|||||||
|
# 更新数据
|
@ -0,0 +1 @@
|
|||||||
|
# 查看
|
@ -0,0 +1,282 @@
|
|||||||
|
# Calendar
|
||||||
|
|
||||||
|
## x-initializer
|
||||||
|
|
||||||
|
- CalendarActionInitializers
|
||||||
|
- TabPaneInitializers
|
||||||
|
- RecordBlockInitializers
|
||||||
|
|
||||||
|
## x-designer
|
||||||
|
|
||||||
|
- CalendarV2.Designer
|
||||||
|
|
||||||
|
## x-settings
|
||||||
|
|
||||||
|
- CalendarSettings
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 日历区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-designer": "CalendarV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "CalendarBlockProvider",
|
||||||
|
"x-acl-action": "users:list",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"paginate": false
|
||||||
|
},
|
||||||
|
"resource": "users",
|
||||||
|
"collection": "users",
|
||||||
|
"fieldNames": {
|
||||||
|
"id": "id",
|
||||||
|
"end": ["f_nxqfedh5fd5"],
|
||||||
|
"start": ["f_nxqfedh5fd5"],
|
||||||
|
"title": "nickname"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"puytl7p5x8o": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "CalendarV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useCalendarBlockProps }}"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"toolBar": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "CalendarV2.ActionBar",
|
||||||
|
"x-initializer": "CalendarActionInitializers",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "494h1j6bxyg",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "CalendarV2.Event",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"View record\") }}",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
"x-component-props": {},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-initializer-props": {
|
||||||
|
"gridInitializer": "RecordBlockInitializers"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t(\"Details\")}}",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-component-props": {},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "RecordBlockInitializers",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-initializer-props": {
|
||||||
|
"actionInitializers": "CalendarFormActionInitializers"
|
||||||
|
},
|
||||||
|
"x-uid": "yty9wg20qq0",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "hcsgenoc3hb",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "c5cpptwxw7s",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "c5iarcnpefc",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "080x8qlpn6s",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "pcche4s596p",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "gk2jk3gpo9f",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关系的日历区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "8q0hsbdr9fc",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2m:list",
|
||||||
|
"x-decorator": "CalendarBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "b",
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"action": "list",
|
||||||
|
"fieldNames": {
|
||||||
|
"id": "id",
|
||||||
|
"start": "createdAt",
|
||||||
|
"title": "f_ex4581dfc0t"
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"paginate": false
|
||||||
|
},
|
||||||
|
"association": "a.o2m"
|
||||||
|
},
|
||||||
|
"x-designer": "CalendarV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "关系的日历区块"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"v3za6qejuz7": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CalendarV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useCalendarBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"toolBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CalendarV2.ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-initializer": "CalendarActionInitializers",
|
||||||
|
"x-uid": "u9ntffsdsrh",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CalendarV2.Event",
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup"
|
||||||
|
},
|
||||||
|
"title": "{{ t(\"View record\") }}",
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"gridInitializer": "RecordBlockInitializers"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t(\"Details\")}}",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"actionInitializers": "CalendarFormActionInitializers"
|
||||||
|
},
|
||||||
|
"x-initializer": "RecordBlockInitializers",
|
||||||
|
"x-uid": "2swhiw4lee5",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "z6odjfbwnht",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "mxr7ikhfc9y",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "mfgucnj7ev8",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "05l5a2h42pn",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "v2jazzvqa85",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
143
packages/core/client/docs/en-US/ui-schema/blocks/data/charts.md
Normal file
143
packages/core/client/docs/en-US/ui-schema/blocks/data/charts.md
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# Charts
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 图表区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"name": "charts"
|
||||||
|
},
|
||||||
|
"x-designer": "ChartV2BlockDesigner",
|
||||||
|
"properties": {
|
||||||
|
"u8841bjm65y": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-decorator": "ChartV2Block",
|
||||||
|
"x-initializer": "ChartInitializers",
|
||||||
|
"x-uid": "j4hzcpzcc3g",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "ldc2aokaop2",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 内嵌区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"name": "charts"
|
||||||
|
},
|
||||||
|
"x-designer": "ChartV2BlockDesigner",
|
||||||
|
"properties": {
|
||||||
|
"g3jq3vkx9fv": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-decorator": "ChartV2Block",
|
||||||
|
"x-initializer": "ChartInitializers",
|
||||||
|
"properties": {
|
||||||
|
"ue7lfigjgm1": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid.Row",
|
||||||
|
"properties": {
|
||||||
|
"43ztg8gu03v": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid.Col",
|
||||||
|
"properties": {
|
||||||
|
"n47fsb4i2lm": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-decorator": "ChartRendererProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"query": {
|
||||||
|
"measures": [
|
||||||
|
{
|
||||||
|
"field": ["id"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dimensions": [],
|
||||||
|
"filter": {
|
||||||
|
"$and": []
|
||||||
|
},
|
||||||
|
"orders": [],
|
||||||
|
"cache": {}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"chartType": "Built-in.line",
|
||||||
|
"general": {
|
||||||
|
"xField": "id",
|
||||||
|
"yField": "id",
|
||||||
|
"smooth": false,
|
||||||
|
"isStack": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"collection": "a",
|
||||||
|
"mode": "builder"
|
||||||
|
},
|
||||||
|
"x-acl-action": "a:list",
|
||||||
|
"x-designer": "ChartRenderer.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"size": "small"
|
||||||
|
},
|
||||||
|
"x-initializer": "ChartInitializers",
|
||||||
|
"properties": {
|
||||||
|
"y25sgh5pukl": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ChartRenderer",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-uid": "ma0s07k753t",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "k6naqcx59ow",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "o8ktlccovml",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "mvn9ai9rhx8",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "mcbyby93j6n",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "sn0lczwtkgm",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
169
packages/core/client/docs/en-US/ui-schema/blocks/data/details.md
Normal file
169
packages/core/client/docs/en-US/ui-schema/blocks/data/details.md
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
# Details
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 详情区块(带分页)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-designer": "DetailsDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "DetailsBlockProvider",
|
||||||
|
"x-acl-action": "users:view",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 1
|
||||||
|
},
|
||||||
|
"rowKey": "id",
|
||||||
|
"resource": "users",
|
||||||
|
"collection": "users",
|
||||||
|
"readPretty": true
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"mjad1dcywip": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Details",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useDetailsBlockProps }}"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"8avswx6qgyp": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-initializer": "DetailsActionInitializers",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "01w17yleng8",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "bgu0ix4lfmc",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Pagination",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useDetailsPaginationProps }}"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "6riuxghrz30",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "3apkfu8ngrp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "yeodhismy2w",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 对多关系区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "2gm2iw937q5",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2m:view",
|
||||||
|
"x-decorator": "DetailsBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 1
|
||||||
|
},
|
||||||
|
"rowKey": "id"
|
||||||
|
},
|
||||||
|
"x-designer": "DetailsDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "关系区块"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"paj8b2noc1g": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Details",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useDetailsBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"3jisjip7503": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "DetailsActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "cbkfxafywtx",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-uid": "d1ls1w6gpzm",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Pagination",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useDetailsPaginationProps }}"
|
||||||
|
},
|
||||||
|
"x-uid": "u0rzv8370za",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "1hfu8jbzzqw",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,286 @@
|
|||||||
|
# Form (Read pretty)
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 单数据详情
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "dt4q817fw81",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a:get",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a",
|
||||||
|
"collection": "a",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "get",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.ReadPrettyDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "Form read pretty"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"30qrxij609o": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "ReadPrettyFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "fznn1ygt50o",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-uid": "t3o4t8fi7l7",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "6dp2qqoc4s0",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关系数据详情
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "jf470wrzdaf",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2oBelongsTo:get",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2oBelongsTo",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2oBelongsTo",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "get",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.ReadPrettyDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "对一关系详情"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"uauk2wbj3iq": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "ReadPrettyFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "r3pcykksp8l",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-uid": "vs000etludp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "rt228uhudcf",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 表格详情
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "lxcjjkkor1m",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2m:get",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "get",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.ReadPrettyDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "表格详情"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"7pe17tcgtye": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "ReadPrettyFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "wek8nc795ja",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-uid": "5y5u8qm6yvz",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "5bpcm982n51",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看关系数据
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "5ymsrq1fv37",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2m:get",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "get",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.ReadPrettyDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "查看关系数据"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"vehcfr0fsh2": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "ReadPrettyFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "zpuz629u2dg",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-uid": "b8u8e2k5inf",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "2rz0u3j14px",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
418
packages/core/client/docs/en-US/ui-schema/blocks/data/form.md
Normal file
418
packages/core/client/docs/en-US/ui-schema/blocks/data/form.md
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
# Form
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 添加表单
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action-props": {
|
||||||
|
"skipScopeCheck": true
|
||||||
|
},
|
||||||
|
"x-acl-action": "a:create",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a",
|
||||||
|
"collection": "a"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"23pj9m7ot5a": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "FormItemInitializers",
|
||||||
|
"x-uid": "0ctuwnatyzd",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "CreateFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"marginTop": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"j7ta88et3mv": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"title": "{{ t(\"Submit\") }}",
|
||||||
|
"x-action": "submit",
|
||||||
|
"x-component": "Action",
|
||||||
|
"x-designer": "Action.Designer",
|
||||||
|
"x-component-props": {
|
||||||
|
"type": "primary",
|
||||||
|
"htmlType": "submit",
|
||||||
|
"useProps": "{{ useCreateActionProps }}"
|
||||||
|
},
|
||||||
|
"x-action-settings": {
|
||||||
|
"triggerWorkflows": []
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-uid": "ua858zswy37",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "o0riryr0ob0",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "1ng4hies4yu",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "ebiyvziafda",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 编辑表单
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action-props": {
|
||||||
|
"skipScopeCheck": false
|
||||||
|
},
|
||||||
|
"x-acl-action": "a:update",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"action": "get",
|
||||||
|
"resource": "a",
|
||||||
|
"collection": "a"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"w533dipin7p": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "FormItemInitializers",
|
||||||
|
"x-uid": "otncpmalhjr",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "UpdateFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"marginTop": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "qjjmm1qkapx",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "n12zo6qd72g",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "zafyvmng9m5",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关系新增表单
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "il5q341k4vg",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action-props": {
|
||||||
|
"skipScopeCheck": true
|
||||||
|
},
|
||||||
|
"x-acl-action": "a.o2m:create",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"action": null,
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "关系新增表单"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"3357yvwe4je": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "FormItemInitializers",
|
||||||
|
"x-uid": "88xhnoml321",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "CreateFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"marginTop": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "dhs8o8vpl1h",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "h6fhh9ox9ay",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关系新增(关系表格里的 Add new)
|
||||||
|
|
||||||
|
useSourceIdFromParentRecord 和 useParamsFromRecord 参数缺失
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "6tknohm4ubr",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action-props": {
|
||||||
|
"skipScopeCheck": true
|
||||||
|
},
|
||||||
|
"x-acl-action": "a.o2m:create",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "关系新增表单-Table"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"o96izt6ez7m": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "FormItemInitializers",
|
||||||
|
"properties": {
|
||||||
|
"pk9l1i26yq6": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid.Row",
|
||||||
|
"properties": {
|
||||||
|
"32wz2lr9cry": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid.Col",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "string",
|
||||||
|
"x-designer": "FormItem.Designer",
|
||||||
|
"x-component": "CollectionField",
|
||||||
|
"x-decorator": "FormItem",
|
||||||
|
"x-collection-field": "b.id",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-uid": "jb09nj4kbfm",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "rtx3b0uom75",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "5gvd7ks6m0z",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "fcw1y2xk6km",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "CreateFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"marginTop": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "msgopb1meoq",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "g0mg6fhho4x",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关系编辑
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "kzg1tz339ro",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action-props": {
|
||||||
|
"skipScopeCheck": false
|
||||||
|
},
|
||||||
|
"x-acl-action": "a.o2m:update",
|
||||||
|
"x-decorator": "FormBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"useSourceId": "{{ useSourceIdFromParentRecord }}",
|
||||||
|
"useParams": "{{ useParamsFromRecord }}",
|
||||||
|
"action": "get",
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m"
|
||||||
|
},
|
||||||
|
"x-designer": "FormV2.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "关系编辑表单"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"3jqysycq8m0": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "FormV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useFormBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "FormItemInitializers",
|
||||||
|
"x-uid": "sy3sjswogh4",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "UpdateFormActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"style": {
|
||||||
|
"marginTop": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "xarcyjpypsy",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "lepez9684rm",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
185
packages/core/client/docs/en-US/ui-schema/blocks/data/gantt.md
Normal file
185
packages/core/client/docs/en-US/ui-schema/blocks/data/gantt.md
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
# Gantt
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 甘特图区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "users:list",
|
||||||
|
"x-decorator": "GanttBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "users",
|
||||||
|
"resource": "users",
|
||||||
|
"action": "list",
|
||||||
|
"fieldNames": {
|
||||||
|
"id": "id",
|
||||||
|
"start": "createdAt",
|
||||||
|
"range": "day",
|
||||||
|
"title": "nickname",
|
||||||
|
"end": "f_46b1u4dp820"
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"paginate": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-designer": "Gantt.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"properties": {
|
||||||
|
"rx21gtpcj3w": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Gantt",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGanttBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"toolBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-initializer": "GanttActionInitializers",
|
||||||
|
"x-uid": "m0uru68ugw9",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "array",
|
||||||
|
"x-decorator": "div",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"style": {
|
||||||
|
"float": "left",
|
||||||
|
"maxWidth": "35%"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-initializer": "TableColumnInitializers",
|
||||||
|
"x-component": "TableV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"rowKey": "id",
|
||||||
|
"rowSelection": {
|
||||||
|
"type": "checkbox"
|
||||||
|
},
|
||||||
|
"useProps": "{{ useTableBlockProps }}",
|
||||||
|
"pagination": false
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"Actions\") }}",
|
||||||
|
"x-action-column": "actions",
|
||||||
|
"x-decorator": "TableV2.Column.ActionBar",
|
||||||
|
"x-component": "TableV2.Column",
|
||||||
|
"x-designer": "TableV2.ActionColumnDesigner",
|
||||||
|
"x-initializer": "TableActionColumnInitializers",
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-decorator": "DndContext",
|
||||||
|
"x-component": "Space",
|
||||||
|
"x-component-props": {
|
||||||
|
"split": "|"
|
||||||
|
},
|
||||||
|
"x-uid": "z2vnizu7bds",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "8gbabo6kdr6",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "5c8g2x6rvro",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Gantt.Event",
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup"
|
||||||
|
},
|
||||||
|
"title": "{{ t(\"View record\") }}",
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t(\"Details\")}}",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "RecordBlockInitializers",
|
||||||
|
"x-uid": "jr5h3asz6gp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "8uwfpgyok9x",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "bjm4sfwxmrx",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "7cmwpc0rrwj",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "nffb7qbs96o",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "hoi2j5lhlho",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "e0rdive4xpa",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,219 @@
|
|||||||
|
# GridCard
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 栅格卡片
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-designer": "GridCard.Designer",
|
||||||
|
"x-component": "BlockItem",
|
||||||
|
"x-decorator": "GridCard.Decorator",
|
||||||
|
"x-acl-action": "users:view",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardBlockItemProps }}"
|
||||||
|
},
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 12
|
||||||
|
},
|
||||||
|
"rowKey": "id",
|
||||||
|
"resource": "users",
|
||||||
|
"collection": "users",
|
||||||
|
"readPretty": true,
|
||||||
|
"runWhenParamsChanged": true
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-initializer": "GridCardActionInitializers",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "prx9wnlzqb1",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "GridCard",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardBlockProps }}"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"type": "object",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "GridCard.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardItemProps }}"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-initializer-props": {
|
||||||
|
"useProps": "{{ useGridCardItemInitializerProps }}"
|
||||||
|
},
|
||||||
|
"x-uid": "pq895sv6136",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-initializer": "GridCardItemActionInitializers",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
"useProps": "{{ useGridCardActionBarProps }}"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "w2c3qcp4nme",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "1ct8yn1jnzn",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "pvnkvtc6tte",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "phodsqwsifq",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关系数据的栅格卡片
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "yr79zvl98lc",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2m:view",
|
||||||
|
"x-decorator": "GridCard.Decorator",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 12
|
||||||
|
},
|
||||||
|
"runWhenParamsChanged": true,
|
||||||
|
"columnCount": {
|
||||||
|
"xs": 1,
|
||||||
|
"md": 12,
|
||||||
|
"lg": 12,
|
||||||
|
"xxl": 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-component": "BlockItem",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardBlockItemProps }}"
|
||||||
|
},
|
||||||
|
"x-designer": "GridCard.Designer",
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "GridCardActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "u36l44beq6a",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "GridCard",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "object",
|
||||||
|
"x-component": "GridCard.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardItemProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"useProps": "{{ useGridCardItemInitializerProps }}"
|
||||||
|
},
|
||||||
|
"x-uid": "aqxlonv3bpk",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actionBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-initializer": "GridCardItemActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useGridCardActionBarProps }}",
|
||||||
|
"layout": "one-column"
|
||||||
|
},
|
||||||
|
"x-uid": "zxhqo81kdf5",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "ed56owlyz1p",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "nfpuvrkwkk7",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
255
packages/core/client/docs/en-US/ui-schema/blocks/data/index.md
Normal file
255
packages/core/client/docs/en-US/ui-schema/blocks/data/index.md
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
# 数据区块概述
|
||||||
|
|
||||||
|
数据可能是某个表(collection)的数据,也可能是某个关系(association)的数据
|
||||||
|
|
||||||
|
- collection,如 a 表
|
||||||
|
- association,如 a.b 关系,关系表为 b
|
||||||
|
|
||||||
|
collection 示例
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"collection": "a"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
association 示例
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.b"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
数据的操作
|
||||||
|
|
||||||
|
```bash
|
||||||
|
<collection>:<action>
|
||||||
|
<collection>.<association>:<action>
|
||||||
|
```
|
||||||
|
|
||||||
|
实际的请求参数
|
||||||
|
|
||||||
|
```
|
||||||
|
<GET|POST> /api/<collection>:<action>
|
||||||
|
<GET|POST> /api/<collection>:<action>/<filterByTk>
|
||||||
|
<GET|POST> /api/<collection>/<sourceId>/<association>:<action>
|
||||||
|
<GET|POST> /api/<collection>/<sourceId>/<association>:<action>/<filterByTk>
|
||||||
|
```
|
||||||
|
|
||||||
|
- collection、association、action 是配置里存好的
|
||||||
|
- sourceId、filterByTk 由上下文提供
|
||||||
|
|
||||||
|
## RecordProvider
|
||||||
|
|
||||||
|
Record
|
||||||
|
|
||||||
|
```ts
|
||||||
|
class Record {
|
||||||
|
protected current = {};
|
||||||
|
protected parent?: Record;
|
||||||
|
public isNew = false;
|
||||||
|
constructor(options = {}) {
|
||||||
|
const { current, parent, isNew } = options;
|
||||||
|
this.current = current || {};
|
||||||
|
this.parent = parent;
|
||||||
|
this.isNew = isNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
a:list(**list 外层不套空 RecordPicker**)
|
||||||
|
|
||||||
|
```jsx | pure
|
||||||
|
{list.map((item) => {
|
||||||
|
const record = new Record({ current: item });
|
||||||
|
return <RecordProvider record={record}></RecordProvider>
|
||||||
|
})}
|
||||||
|
```
|
||||||
|
|
||||||
|
a:get(view、edit)
|
||||||
|
|
||||||
|
```jsx | pure
|
||||||
|
const record = new Record({ current: item });
|
||||||
|
<RecordProvider record={item}></RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
a:create
|
||||||
|
|
||||||
|
```jsx | pure
|
||||||
|
const record = new Record({ current, isNew: true });
|
||||||
|
<RecordProvider record={record}></RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
a.b:list
|
||||||
|
|
||||||
|
```jsx | pure
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
{list.map((item) => {
|
||||||
|
const recordB = new Record({
|
||||||
|
current: item,
|
||||||
|
});
|
||||||
|
return <RecordProvider record={recordB}></RecordProvider>
|
||||||
|
})}
|
||||||
|
</RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
a.b:get
|
||||||
|
|
||||||
|
```jsx | pure
|
||||||
|
const recordA = new Record({
|
||||||
|
current: itemA,
|
||||||
|
});
|
||||||
|
const recordB = new Record({
|
||||||
|
current: itemB,
|
||||||
|
parent: recordA,
|
||||||
|
});
|
||||||
|
// 或者
|
||||||
|
recordB.setParent(recordA);
|
||||||
|
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
<RecordProvider record={recordB}></RecordProvider>
|
||||||
|
</RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
a.b:create
|
||||||
|
|
||||||
|
```jsx | pure
|
||||||
|
const recordA = new Record({
|
||||||
|
current: itemA,
|
||||||
|
});
|
||||||
|
const recordB = new Record({
|
||||||
|
isNew: true,
|
||||||
|
parent: recordA,
|
||||||
|
});
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
<RecordProvider record={recordB}></RecordProvider>
|
||||||
|
</RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 区块
|
||||||
|
|
||||||
|
以下重要组件说明
|
||||||
|
|
||||||
|
- `DataBlockProvider` 数据区块的总称
|
||||||
|
- `BlockProvider` 区块的各种参数设置
|
||||||
|
- `CollectionProvider` collection 信息
|
||||||
|
- `AssociationProvider` association 信息
|
||||||
|
- `UseRequestProvider` useRequest 的 result
|
||||||
|
|
||||||
|
没有当前记录的区块,如表格
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<DataBlockProvider>
|
||||||
|
<ActionBar>
|
||||||
|
<Action type="filter" />
|
||||||
|
<Action type="create" />
|
||||||
|
<Action type="delete" />
|
||||||
|
</ActionBar>
|
||||||
|
<Table useProps />
|
||||||
|
<Pagination />
|
||||||
|
<DataBlockProvider/>
|
||||||
|
```
|
||||||
|
|
||||||
|
DataBlockProvider 内容
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<BlockProvider>
|
||||||
|
<CollectionProvider>
|
||||||
|
<UseRequestProvider>
|
||||||
|
{props.children}
|
||||||
|
</UseRequestProvider>
|
||||||
|
</CollectionProvider>
|
||||||
|
<BlockProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
有当前记录的区块,如表单
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<DataBlockProvider>
|
||||||
|
<Form useProps />
|
||||||
|
<DataBlockProvider/>
|
||||||
|
```
|
||||||
|
|
||||||
|
DataBlockProvider 内容
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<BlockProvider>
|
||||||
|
<CollectionProvider>
|
||||||
|
<UseRequestProvider>
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
{props.children}
|
||||||
|
</RecordProvider>
|
||||||
|
</UseRequestProvider>
|
||||||
|
</CollectionProvider>
|
||||||
|
</BlockProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
有当前父级记录的区块,如对多关系的表格区块,区块本身不套 RecordPicker
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
<DataBlockProvider>
|
||||||
|
<ActionBar>
|
||||||
|
<Action type="filter" />
|
||||||
|
<Action type="create" />
|
||||||
|
<Action type="delete" />
|
||||||
|
</ActionBar>
|
||||||
|
<Table useProps />
|
||||||
|
<Pagination />
|
||||||
|
</DataBlockProvider>
|
||||||
|
<DataBlockProvider>
|
||||||
|
<ActionBar>
|
||||||
|
<Action type="filter" />
|
||||||
|
<Action type="create" />
|
||||||
|
<Action type="delete" />
|
||||||
|
</ActionBar>
|
||||||
|
<Table useProps />
|
||||||
|
<Pagination />
|
||||||
|
</DataBlockProvider>
|
||||||
|
</RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
DataBlockProvider 内容
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<BlockProvider>
|
||||||
|
<AssociationProvider>
|
||||||
|
<CollectionProvider>
|
||||||
|
<UseRequestProvider>
|
||||||
|
{props.children}
|
||||||
|
</UseRequestProvider>
|
||||||
|
</CollectionProvider>
|
||||||
|
</AssociationProvider>
|
||||||
|
</BlockProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
有父级记录也有当前记录的区块,如关系的表单
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
<DataBlockProvider>
|
||||||
|
<Form useProps />
|
||||||
|
</DataBlockProvider>
|
||||||
|
<DataBlockProvider>
|
||||||
|
<Form useProps />
|
||||||
|
</DataBlockProvider>
|
||||||
|
</RecordProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
DataBlockProvider 内容
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<BlockProvider>
|
||||||
|
<AssociationProvider>
|
||||||
|
<CollectionProvider>
|
||||||
|
<UseRequestProvider>
|
||||||
|
<RecordProvider record={recordA}>
|
||||||
|
{props.children}
|
||||||
|
</RecordProvider>
|
||||||
|
</UseRequestProvider>
|
||||||
|
</CollectionProvider>
|
||||||
|
</AssociationProvider>
|
||||||
|
</BlockProvider>
|
||||||
|
```
|
160
packages/core/client/docs/en-US/ui-schema/blocks/data/kanban.md
Normal file
160
packages/core/client/docs/en-US/ui-schema/blocks/data/kanban.md
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# Kanban
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 看板区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "t_ehwqkw0ljw2:list",
|
||||||
|
"x-decorator": "KanbanBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "t_ehwqkw0ljw2",
|
||||||
|
"resource": "t_ehwqkw0ljw2",
|
||||||
|
"action": "list",
|
||||||
|
"groupField": "f_5h4umwf59lc",
|
||||||
|
"params": {
|
||||||
|
"sort": ["f_5h4umwf59lc_sort"],
|
||||||
|
"paginate": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-designer": "Kanban.Designer",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "KanbanActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "4vh5t2iohwp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"iwnc45ou960": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "Kanban",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useKanbanBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"card": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-label-disabled": true,
|
||||||
|
"x-decorator": "BlockItem",
|
||||||
|
"x-component": "Kanban.Card",
|
||||||
|
"x-component-props": {
|
||||||
|
"openMode": "drawer"
|
||||||
|
},
|
||||||
|
"x-designer": "Kanban.Card.Designer",
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-component-props": {
|
||||||
|
"dndContext": false
|
||||||
|
},
|
||||||
|
"x-uid": "3v010y09684",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "dk9a5cb9d95",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"cardViewer": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"View\") }}",
|
||||||
|
"x-designer": "Action.Designer",
|
||||||
|
"x-component": "Kanban.CardViewer",
|
||||||
|
"x-action": "view",
|
||||||
|
"x-component-props": {
|
||||||
|
"openMode": "drawer"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"View record\") }}",
|
||||||
|
"x-component": "Action.Container",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t(\"Details\")}}",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "RecordBlockInitializers",
|
||||||
|
"x-uid": "1lhiyyydpye",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "gisuj0zldz1",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "ohly2w4gnwp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "29hjrxcfczh",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "0k56h1q80xo",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "uny2d4n5ry4",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "3gga5lop4t6",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
213
packages/core/client/docs/en-US/ui-schema/blocks/data/list.md
Normal file
213
packages/core/client/docs/en-US/ui-schema/blocks/data/list.md
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
# List
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 列表区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "h9mzrnxokk3",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a:view",
|
||||||
|
"x-decorator": "List.Decorator",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a",
|
||||||
|
"collection": "a",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 10
|
||||||
|
},
|
||||||
|
"runWhenParamsChanged": true,
|
||||||
|
"rowKey": "id"
|
||||||
|
},
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-designer": "List.Designer",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "List 区块"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "ListActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "chjgpgd45qz",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "List",
|
||||||
|
"x-component-props": {
|
||||||
|
"props": "{{ useListBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "object",
|
||||||
|
"x-component": "List.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useListItemProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"useProps": "{{ useListItemInitializerProps }}"
|
||||||
|
},
|
||||||
|
"x-uid": "u45nwmwlone",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actionBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-initializer": "ListItemActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useListActionBarProps }}",
|
||||||
|
"layout": "one-column"
|
||||||
|
},
|
||||||
|
"x-uid": "9erso7nhotq",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "qkuzlun8frh",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "1mfaa48qy5j",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 对多关系的列表区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"x-uid": "6tzpnkz60rx",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "a.o2m:view",
|
||||||
|
"x-decorator": "List.Decorator",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"collection": "b",
|
||||||
|
"association": "a.o2m",
|
||||||
|
"readPretty": true,
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 10
|
||||||
|
},
|
||||||
|
"runWhenParamsChanged": true
|
||||||
|
},
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-designer": "List.Designer",
|
||||||
|
"x-component-props": {
|
||||||
|
"title": "对多关系的列表区块"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "ListActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "i05xr7nfs26",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "List",
|
||||||
|
"x-component-props": {
|
||||||
|
"props": "{{ useListBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "object",
|
||||||
|
"x-component": "List.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useListItemProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "ReadPrettyFormItemInitializers",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"useProps": "{{ useListItemInitializerProps }}"
|
||||||
|
},
|
||||||
|
"x-uid": "krerg70a28e",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"actionBar": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-initializer": "ListItemActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useListActionBarProps }}",
|
||||||
|
"layout": "one-column"
|
||||||
|
},
|
||||||
|
"x-uid": "9745q36ltfv",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "yxc0lnax9gk",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "6mlzb7p6dvn",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
115
packages/core/client/docs/en-US/ui-schema/blocks/data/map.md
Normal file
115
packages/core/client/docs/en-US/ui-schema/blocks/data/map.md
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Map
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 地图区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "t_ddo5bd0swdg:list",
|
||||||
|
"x-decorator": "MapBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "t_ddo5bd0swdg",
|
||||||
|
"resource": "t_ddo5bd0swdg",
|
||||||
|
"action": "list",
|
||||||
|
"fieldNames": {
|
||||||
|
"field": ["f_tmhy5bbusr8"]
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"paginate": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-designer": "MapBlockDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "MapActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "ap0xzy32bms",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"1672zefmplu": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "MapBlock",
|
||||||
|
"x-component-props": {
|
||||||
|
"useProps": "{{ useMapBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup"
|
||||||
|
},
|
||||||
|
"title": "{{ t(\"View record\") }}",
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{t(\"Details\")}}",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
"x-component-props": {},
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "RecordBlockInitializers",
|
||||||
|
"x-uid": "vce3u8hfdrf",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "waw7z3qw7nn",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "szyybarlapl",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "83m07ksqbal",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "5cquhqq2161",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "p42awfpfnbf",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
193
packages/core/client/docs/en-US/ui-schema/blocks/data/table.md
Normal file
193
packages/core/client/docs/en-US/ui-schema/blocks/data/table.md
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
# Table
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 表格区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-designer": "TableBlockDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "TableBlockProvider",
|
||||||
|
"x-acl-action": "users:list",
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"rowKey": "id",
|
||||||
|
"dragSort": false,
|
||||||
|
"resource": "users",
|
||||||
|
"showIndex": true,
|
||||||
|
"collection": "users",
|
||||||
|
"disableTemplate": false
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-initializer": "TableActionInitializers",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "55tgsn1puqk",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"g22il5nkv67": {
|
||||||
|
"type": "array",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "TableV2",
|
||||||
|
"x-initializer": "TableColumnInitializers",
|
||||||
|
"x-component-props": {
|
||||||
|
"rowKey": "id",
|
||||||
|
"useProps": "{{ useTableBlockProps }}",
|
||||||
|
"rowSelection": {
|
||||||
|
"type": "checkbox"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"Actions\") }}",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-designer": "TableV2.ActionColumnDesigner",
|
||||||
|
"x-component": "TableV2.Column",
|
||||||
|
"x-decorator": "TableV2.Column.ActionBar",
|
||||||
|
"x-initializer": "TableActionColumnInitializers",
|
||||||
|
"x-action-column": "actions",
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"type": "void",
|
||||||
|
"version": "2.0",
|
||||||
|
"x-component": "Space",
|
||||||
|
"x-decorator": "DndContext",
|
||||||
|
"x-component-props": {
|
||||||
|
"split": "|"
|
||||||
|
},
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"x-uid": "qkcugf5l0h6",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "kbgbskxsj3j",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "6r8e3mhnuxg",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "b87bbc5x0cp",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 对多关系区块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-decorator": "TableBlockProvider",
|
||||||
|
"x-acl-action": "a.o2m:list",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "b",
|
||||||
|
"resource": "a.o2m",
|
||||||
|
"action": "list",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"showIndex": true,
|
||||||
|
"dragSort": false,
|
||||||
|
"disableTemplate": false,
|
||||||
|
"association": "a.o2m"
|
||||||
|
},
|
||||||
|
"x-designer": "TableBlockDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-initializer": "TableActionInitializers",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "0gwze38mzps",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
},
|
||||||
|
"0wuk1cpy5zp": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "array",
|
||||||
|
"x-initializer": "TableColumnInitializers",
|
||||||
|
"x-component": "TableV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"rowKey": "id",
|
||||||
|
"rowSelection": {
|
||||||
|
"type": "checkbox"
|
||||||
|
},
|
||||||
|
"useProps": "{{ useTableBlockProps }}"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"title": "{{ t(\"Actions\") }}",
|
||||||
|
"x-action-column": "actions",
|
||||||
|
"x-decorator": "TableV2.Column.ActionBar",
|
||||||
|
"x-component": "TableV2.Column",
|
||||||
|
"x-designer": "TableV2.ActionColumnDesigner",
|
||||||
|
"x-initializer": "TableActionColumnInitializers",
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-decorator": "DndContext",
|
||||||
|
"x-component": "Space",
|
||||||
|
"x-component-props": {
|
||||||
|
"split": "|"
|
||||||
|
},
|
||||||
|
"x-uid": "gq0nkc7rf0q",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "zx97354nzfq",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "nss8uocacdq",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "0j5kcrius5z",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,41 @@
|
|||||||
|
# Collapse
|
||||||
|
|
||||||
|
## UI Schema
|
||||||
|
|
||||||
|
### 折叠面板
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-decorator": "AssociationFilter.Provider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"collection": "users",
|
||||||
|
"blockType": "filter",
|
||||||
|
"associationFilterStyle": {
|
||||||
|
"width": "100%"
|
||||||
|
},
|
||||||
|
"name": "filter-collapse"
|
||||||
|
},
|
||||||
|
"x-designer": "AssociationFilter.BlockDesigner",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"properties": {
|
||||||
|
"x8q0r7wp73e": {
|
||||||
|
"_isJSONSchemaObject": true,
|
||||||
|
"version": "2.0",
|
||||||
|
"type": "void",
|
||||||
|
"x-action": "associateFilter",
|
||||||
|
"x-initializer": "AssociationFilter.FilterBlockInitializer",
|
||||||
|
"x-component": "AssociationFilter",
|
||||||
|
"x-uid": "j11suxuizte",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-uid": "0g6pyonrj83",
|
||||||
|
"x-async": false,
|
||||||
|
"x-index": 1
|
||||||
|
}
|
||||||
|
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user