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: {
|
||||
...umiConfig.alias,
|
||||
},
|
||||
ssr: {
|
||||
|
||||
},
|
||||
exportStatic: {
|
||||
ignorePreRenderError: true
|
||||
},
|
||||
cacheDirectoryPath: `node_modules/.docs-${lang}-cache`,
|
||||
outputPath: `./docs/dist/${lang}`,
|
||||
resolve: {
|
||||
|
6
.github/workflows/deploy-client-docs.yml
vendored
6
.github/workflows/deploy-client-docs.yml
vendored
@ -23,8 +23,10 @@ jobs:
|
||||
with:
|
||||
node-version: "18"
|
||||
- run: yarn install
|
||||
- name: Build
|
||||
run: yarn doc build core/client
|
||||
- name: Build zh-CN
|
||||
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
|
||||
id: set-tags
|
||||
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_SCHEMA: ${{ matrix.schema }}
|
||||
COLLECTION_MANAGER_SCHEMA: ${{ matrix.collection_schema }}
|
||||
timeout-minutes: 40
|
||||
timeout-minutes: 60
|
||||
|
@ -206,14 +206,14 @@ type ConditionFunc = (ctx: any) => Promise<boolean> | boolean;
|
||||
**Detailed Information**
|
||||
|
||||
* resourceName - Name of the resource
|
||||
* actionNames - Name of the resource action
|
||||
* actionNames - Name of the resource action
|
||||
* condition? - Configuration of the validity condition
|
||||
* Pass in a `string` to use a condition that is already defined; Use the `acl.allowManager.registerCondition` method to register a condition.
|
||||
```typescript
|
||||
acl.allowManager.registerAllowCondition('superUser', async () => {
|
||||
return ctx.state.user?.id === 1;
|
||||
});
|
||||
|
||||
|
||||
// Open permissions of the users:list with validity condition superUser
|
||||
acl.allow('users', 'list', 'superUser');
|
||||
```
|
||||
|
@ -35,7 +35,6 @@ Add Providers, build-in Providers are:
|
||||
- SystemSettingsProvider
|
||||
- PluginManagerProvider
|
||||
- SchemaComponentProvider
|
||||
- SchemaInitializerProvider
|
||||
- BlockSchemaComponentProvider
|
||||
- AntdSchemaComponentProvider
|
||||
- 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
|
||||
- PluginManagerProvider
|
||||
- SchemaComponentProvider
|
||||
- SchemaInitializerProvider
|
||||
- BlockSchemaComponentProvider
|
||||
- AntdSchemaComponentProvider
|
||||
- DocumentTitleProvider
|
||||
|
@ -25,15 +25,11 @@
|
||||
|
||||
```tsx |pure
|
||||
import React, { useContext } from 'react';
|
||||
import { SchemaInitializerContext } from '@nocobase/client';
|
||||
import { Plugin } from '@nocobase/client';
|
||||
|
||||
export default React.memo((props) => {
|
||||
const items = useContext(SchemaInitializerContext);
|
||||
const BlockInitializers = {};
|
||||
return (
|
||||
<SchemaInitializerContext.Provider value={{ ...items, BlockInitializers }}>
|
||||
{props.children}
|
||||
</SchemaInitializerContext.Provider>
|
||||
);
|
||||
});
|
||||
class MyPlugin extends Plugin {
|
||||
async load() {
|
||||
this.app.schemaInitializerManager.add('BlockInitializers', BlockInitializers)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -35,7 +35,6 @@ const app = new Application({
|
||||
- SystemSettingsProvider
|
||||
- PluginManagerProvider
|
||||
- SchemaComponentProvider
|
||||
- SchemaInitializerProvider
|
||||
- BlockSchemaComponentProvider
|
||||
- AntdSchemaComponentProvider
|
||||
- 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
|
||||
- PluginManagerProvider
|
||||
- SchemaComponentProvider
|
||||
- SchemaInitializerProvider
|
||||
- BlockSchemaComponentProvider
|
||||
- AntdSchemaComponentProvider
|
||||
- DocumentTitleProvider
|
||||
|
@ -23,17 +23,13 @@
|
||||
|
||||
## 替换
|
||||
|
||||
```tsx |pure
|
||||
```tsx | pure
|
||||
import React, { useContext } from 'react';
|
||||
import { SchemaInitializerContext } from '@nocobase/client';
|
||||
import { Plugin } from '@nocobase/client';
|
||||
|
||||
export default React.memo((props) => {
|
||||
const items = useContext(SchemaInitializerContext);
|
||||
const BlockInitializers = {};
|
||||
return (
|
||||
<SchemaInitializerContext.Provider value={{ ...items, BlockInitializers }}>
|
||||
{props.children}
|
||||
</SchemaInitializerContext.Provider>
|
||||
);
|
||||
});
|
||||
class MyPlugin extends Plugin {
|
||||
async load() {
|
||||
this.app.schemaInitializerManager.add('BlockInitializers', BlockInitializers)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
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",
|
||||
"commander": "^9.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-testing-library": "^5.11.0",
|
||||
"execa": "^5.1.1",
|
||||
@ -103,7 +103,7 @@
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "5.1.3",
|
||||
"vite": "^4.4.9",
|
||||
"vitest": "^0.34.3"
|
||||
"vitest": "^0.34.6"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.14.2",
|
||||
|
@ -24,7 +24,13 @@ export async function build(pkgs: string[]) {
|
||||
|
||||
const packages = getPackages(pkgs);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3,19 +3,29 @@ import { defineConfig } from 'dumi';
|
||||
import { defineThemeConfig } from 'dumi-theme-nocobase';
|
||||
|
||||
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({
|
||||
hash: true,
|
||||
alias: {
|
||||
...umiConfig.alias,
|
||||
},
|
||||
// ssr: {},
|
||||
// exportStatic: {
|
||||
// ignorePreRenderError: true
|
||||
// },
|
||||
cacheDirectoryPath: `node_modules/.docs-client-${lang}-cache`,
|
||||
outputPath: `./dist/${lang}`,
|
||||
resolve: {
|
||||
atomDirs: [
|
||||
{ type: 'api', dir: 'src' },
|
||||
{ type: 'api', dir: 'src/schema-component/antd' },
|
||||
{ type: 'api', dir: 'src/route-switch/antd' },
|
||||
],
|
||||
docDirs: [`./docs/${lang}`]
|
||||
},
|
||||
locales: [
|
||||
{ id: 'en-US', name: 'English' },
|
||||
{ id: 'zh-CN', name: '中文' },
|
||||
],
|
||||
themeConfig: defineThemeConfig({
|
||||
title: 'NocoBase',
|
||||
logo: 'https://www.nocobase.com/images/logo.png',
|
||||
@ -25,92 +35,338 @@ export default defineConfig({
|
||||
nav: [
|
||||
{
|
||||
title: 'API',
|
||||
link: '/apis/application',
|
||||
link: '/core/application/application',
|
||||
},
|
||||
// {
|
||||
// title: 'UI Schema',
|
||||
// link: '/ui-schema',
|
||||
// },
|
||||
],
|
||||
sidebarEnhance: {
|
||||
'/apis': [
|
||||
'/core': [
|
||||
{
|
||||
title: 'Core',
|
||||
title: 'Application',
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
title: 'Application',
|
||||
children: [
|
||||
{
|
||||
title: 'Application',
|
||||
link: '/apis/application',
|
||||
},
|
||||
{
|
||||
title: 'APIClient',
|
||||
link: '/apis/api-client',
|
||||
},
|
||||
{
|
||||
title: 'PluginSettingsManager',
|
||||
link: '#',
|
||||
},
|
||||
],
|
||||
link: '/core/application/application',
|
||||
},
|
||||
{
|
||||
title: 'UI schema designer',
|
||||
children: [
|
||||
{
|
||||
title: 'SchemaComponent',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'SchemaInitializer',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'SchemaSettings',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'DNDContext & DragHandler',
|
||||
link: '#',
|
||||
},
|
||||
],
|
||||
title: 'Plugin',
|
||||
link: '/core/application/plugin',
|
||||
},
|
||||
{
|
||||
title: 'Collection Manager',
|
||||
link: '#',
|
||||
title: 'PluginManager',
|
||||
link: '/core/application/plugin-manager',
|
||||
},
|
||||
{
|
||||
title: 'BlockProvider',
|
||||
link: '#',
|
||||
title: 'RouterManager',
|
||||
link: '/core/application/router-manager',
|
||||
},
|
||||
{
|
||||
title: 'RecordProvider',
|
||||
link: '#',
|
||||
title: 'PluginSettingsManager',
|
||||
link: '/core/application/plugin-settings-manager',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'React components',
|
||||
title: 'UI Schema',
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
title: 'Board',
|
||||
link: '#',
|
||||
title: 'SchemaComponent',
|
||||
link: '/core/ui-schema/schema-component',
|
||||
},
|
||||
{
|
||||
title: 'Icon',
|
||||
link: '#',
|
||||
title: 'Designable',
|
||||
link: '/core/ui-schema/designable',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Schema components',
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
title: 'Input',
|
||||
link: '#',
|
||||
title: 'SchemaInitializer',
|
||||
link: '/core/ui-schema/schema-initializer',
|
||||
},
|
||||
{
|
||||
title: 'SchemaInitializerManager',
|
||||
link: '/core/ui-schema/schema-initializer-manager',
|
||||
},
|
||||
{
|
||||
title: 'SchemaSettings',
|
||||
link: '/core/ui-schema/schema-settings',
|
||||
},
|
||||
{
|
||||
title: 'SchemaSettingsManager',
|
||||
link: '/core/ui-schema/schema-settings-manager',
|
||||
},
|
||||
{
|
||||
title: 'SchemaToolbar',
|
||||
link: '/core/ui-schema/schema-toolbar',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
// '/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
|
||||
/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
|
||||
}
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user