diff --git a/fis-conf.js b/fis-conf.js index 4fff5754..8bb27c1b 100644 --- a/fis-conf.js +++ b/fis-conf.js @@ -243,9 +243,13 @@ if (fis.project.currentMedia() === 'publish') { return contents; } - return contents.replace(/(\\?(?:'|"))\/api\/mock2?\//g, function(_, qutoa) { - return qutoa + 'https://houtai.baidu.com/api/mock2/'; - }); + return contents + .replace(/(\\?(?:'|"))((?:get|post|delete|put)\:)?\/api\/mock2?/ig, function(_, qutoa, method) { + return qutoa + (method || '') + 'https://houtai.baidu.com/api/mock2'; + }) + .replace(/(\\?(?:'|"))((?:get|post|delete|put)\:)?\/api\/sample/ig, function(_, qutoa, method) { + return qutoa + (method || '') + 'https://houtai.baidu.com/api/sample'; + }); } }) diff --git a/gh-pages/docs/advanced.js b/gh-pages/docs/advanced.js index b385793c..51d6817f 100644 --- a/gh-pages/docs/advanced.js +++ b/gh-pages/docs/advanced.js @@ -3,7 +3,7 @@ define('docs/advanced.md', function(require, exports, module) { module.exports = { "title": "高级用法", "shortname": "advanced", - "html": "

在开始阅读之前,希望你已经阅读 快速开始文档

\n

数据作用域

配置中很多地方都可以用变量如: tpl 类型的渲染器、API 中的 Url、FormItem 中的 source 配置、visibleOn、disabledOn 以及 Form 中的 redirect 配置等等。

\n

那么都有哪些数据可以用?这取决于在哪个容器,关于容器中的数据说明如下:

\n\n

取值过程,也跟 JS 作用域中取值一样,当前作用域中有,则直接返回当前作用域中,如果没有当前作用域没有,会一直往上找,直到找到了为止。如果存在同名变量,则返回就近作用域中数据。

\n

需要注意的是,要取到值一定是在自己所在的作用域,或者上级作用域里面,同级的是取不到的,如果需要怎么办?可以往下看联动,比如:FormA 的数据发送给 formB, 另外一种方式,可以把接口拉取换到父级组件去操作,没有可拉取数据的组件,就一起包在一个 service 控件里面。

\n

联动

简单的显隐联动

主要通过 visibleOnhiddenOndisabledOn 来配置。

\n
\n

选项联动

比如 select 中 options 可能根据某个值不同而不同。

\n
\n

他们是怎么关联的呢?注意看 select 的 source 配置 "/api/mock/getOptions?waitSeconds=1&type=$foo" 这里用了变量 $foo 这个 foo 正好是第一个表单的 name 值。只要这个值发生变化,source 就会重新获取一次。

\n

这里有个问题就是,数据一旦变化就会出发重新拉取,如果是输入框岂不是拉取得很频繁?没关系,也可以主动拉取如:

\n
\n

注意,source 中的传参是通过 source 中的 data 关联的,不能写在 source 的 url 中,因为如果写了,就会自动监控值的变化而自动刷新,写在 data 里面关联则不会。如果对 source 中的配置规则不了解,请前往 API 说明

\n

另外注意 button 的 target 值,正好是这个 form 的 name 值 lidong 的 formItem 的 name 值 select。当按钮的对象是一个 formItem 时,会出发 formItem 的数据重新拉取。

\n

数据联动

Form 和 CRUD, CRUD 有个 filter 配置项,里面可以配置表单项,当他提交时 CRUD 自动就会携带接受到的表单数据然后重新获取数据。有个限制,就是 CRUD 和 filter 必须放在一起,不能分开,实际上完全可以分开,只要 Form 的 target 是 CRUD 的 name 值即可。

\n
\n

Form 的 target 还可以是另外一个 Form,当 A Form 把自己的数据提交给 B Form 时,A 的数据会被合并到 B Form 中,同时,B Form 会再次初始化,如:拉取 initApi, 重新拉取 formItem 上的 source 等等。 比如用户管理中的加入用户操作就是用这种方式实现的。

\n
\n", + "html": "

在开始阅读之前,希望你已经阅读 快速开始文档

\n

数据作用域

配置中很多地方都可以用变量如: tpl 类型的渲染器、API 中的 Url、FormItem 中的 source 配置、visibleOn、disabledOn 以及 Form 中的 redirect 配置等等。

\n

那么都有哪些数据可以用?这取决于在哪个容器,关于容器中的数据说明如下:

\n\n

取值过程,也跟 JS 作用域中取值一样,当前作用域中有,则直接返回当前作用域中,如果没有当前作用域没有,会一直往上找,直到找到了为止。如果存在同名变量,则返回就近作用域中数据。

\n

需要注意的是,要取到值一定是在自己所在的作用域,或者上级作用域里面,同级的是取不到的,如果需要怎么办?可以往下看联动,比如:FormA 的数据发送给 formB, 另外一种方式,可以把接口拉取换到父级组件去操作,没有可拉取数据的组件,就一起包在一个 service 控件里面。

\n

联动

简单的显隐联动

主要通过 visibleOnhiddenOndisabledOn 来配置。

\n
\n

选项联动

比如 select 中 options 可能根据某个值不同而不同。

\n
\n

他们是怎么关联的呢?注意看 select 的 source 配置 "/api/mock/getOptions?waitSeconds=1&type=$foo" 这里用了变量 $foo 这个 foo 正好是第一个表单的 name 值。只要这个值发生变化,source 就会重新获取一次。

\n

这里有个问题就是,数据一旦变化就会出发重新拉取,如果是输入框岂不是拉取得很频繁?没关系,也可以主动拉取如:

\n
\n

注意,source 中的传参是通过 source 中的 data 关联的,不能写在 source 的 url 中,因为如果写了,就会自动监控值的变化而自动刷新,写在 data 里面关联则不会。如果对 source 中的配置规则不了解,请前往 API 说明

\n

另外注意 button 的 target 值,正好是这个 form 的 name 值 lidong 的 formItem 的 name 值 select。当按钮的对象是一个 formItem 时,会出发 formItem 的数据重新拉取。

\n

数据联动

Form 和 CRUD, CRUD 有个 filter 配置项,里面可以配置表单项,当他提交时 CRUD 自动就会携带接受到的表单数据然后重新获取数据。有个限制,就是 CRUD 和 filter 必须放在一起,不能分开,实际上完全可以分开,只要 Form 的 target 是 CRUD 的 name 值即可。

\n
\n

Form 的 target 还可以是另外一个 Form,当 A Form 把自己的数据提交给 B Form 时,A 的数据会被合并到 B Form 中,同时,B Form 会再次初始化,如:拉取 initApi, 重新拉取 formItem 上的 source 等等。 比如用户管理中的加入用户操作就是用这种方式实现的。

\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/dev.js b/gh-pages/docs/dev.js index 7e7fc0fa..aa07d81d 100644 --- a/gh-pages/docs/dev.js +++ b/gh-pages/docs/dev.js @@ -1,47 +1,48 @@ define('docs/dev.md', function(require, exports, module) { - module.exports = { - title: '自定义组件', - shortname: 'dev', - html: - '

自定义组件主要分两类。表单类和非表单类。

\n

FormItem

即表单类,它主要用来扩充表单项。先看个例子。

\n
import * as React from \'react\';\nimport {FormItem} from \'amis\';\nimport * as cx from \'classnames\';\n\n@FormItem({\n    type: \'custom-checkbox\',\n})\nexport default class CustomCheckbox extends React.PureComponent {\n    toggle = () => {\n        const {value, onChange} = this.props;\n\n        onChange(!value);\n    };\n\n    render() {\n        const {value} = this.props;\n        const checked = !!value;\n\n        return (\n            <div>\n                <a\n                    className={cx(\'btn btn-default\', {\n                        \'btn-success\': checked,\n                    })}\n                    onClick={this.toggle}\n                >\n                    {checked ? \'已勾选\' : \'请勾选\'}\n                </a>\n                <div className="inline m-l-xs">{checked ? \'已勾选\' : \'请勾选\'}</div>\n            </div>\n        );\n    }\n}\n
\n

有了这个代码后,页面配置 form 的 controls 里面就可以通过这样的配置启动了。

\n
{\n    // 其他信息省略了。。\n    type: \'form\',\n    controls: [\n        {\n            type: \'custom-checkbox\',\n            name: \'变量名\',\n            label: \'自定义组件。\'\n        }\n    ]\n}\n
\n

表单项开发主要关心两件事。

\n
    \n
  1. 呈现当前值。如以上例子,勾选了则显示已勾选,否则显示请勾选
  2. \n
  3. 接收用户交互,修改表单项值。如以上例子,当用户点击按钮时,切换当前选中的值。
  4. \n
\n

至于其他功能如:label/description 的展示、表单验证功能、表单布局(常规、左右或者内联)等等,只要是通过 FormItem 注册进去的都无需自己实现。

\n

Renderer

非表单类的组件自定义,主要通过 Renderer 实现。在开始阅读之前,请先阅读 AMis 工作原理

\n
import * as React from \'react\';\nimport {Renderer} from \'amis\';\n\n@Renderer({\n    test: /(^|\\/)my\\-renderer$/,\n})\nclass CustomRenderer extends React.Component {\n    render() {\n        const {tip, body, render} = this.props;\n\n        return (\n            <div>\n                <p>这是自定义组件:{tip}</p>\n                {body ? (\n                    <div className="container">\n                        {render(\'body\', body, {\n                            // 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个\n                        })}\n                    </div>\n                ) : null}\n            </div>\n        );\n    }\n}\n
\n

这里注册一个 React 组件,当节点的 path 信息是 my-renderer 结尾时,交给当前组件来完成渲染。

\n

请注意 this.props 中的 render 方法,它用来实现容器功能,通过它可以让使用者动态的配置其他渲染模型。

\n

工具

目前主要提供以下工具。

\n

fetch

import {fetch} from \'amis/utils\';\n
\n

用来做 ajax 请求。参数说明

\n\n

返回一个 Promise。

\n

如:

\n
import {fetch} from \'amis/utils\';\n\nfetch(\'http://www.baidu.com/api/xxx?a=${a}&b=${b}\', {\n    a: \'aa\',\n    b: \'bb\',\n}).then(function(result) {\n    console.log(result);\n});\n
\n

filter

import {filter} from \'amis/utils\';\n
\n

主要用来做字符替换,如:

\n
import {filter} from \'amis/utils\';\n\nfilter(\'blabla?a={a}\', {a: 123}); // => \'blabla?a=123\'\n
\n', - toc: { - label: '目录', - type: 'toc', - children: [ - { - label: 'FormItem', - fragment: 'formitem', - fullPath: '#formitem', - level: 3, - }, - { - label: 'Renderer', - fragment: 'renderer', - fullPath: '#renderer', - level: 3, - }, - { - label: '工具', - fragment: '%E5%B7%A5%E5%85%B7', - fullPath: '#%E5%B7%A5%E5%85%B7', - level: 2, - children: [ - { - label: 'fetch', - fragment: 'fetch', - fullPath: '#fetch', - level: 3, - }, - { - label: 'filter', - fragment: 'filter', - fullPath: '#filter', - level: 3, - }, - ], - }, - ], - level: 0, + + module.exports = { + "title": "自定义组件", + "shortname": "dev", + "html": "

自定义组件主要分两类。表单类和非表单类。

\n

FormItem

即表单类,它主要用来扩充表单项。先看个例子。

\n
import * as React from 'react';\nimport {FormItem} from 'amis';\nimport * as cx from 'classnames';\n\n@FormItem({\n    type: 'custom-checkbox',\n})\nexport default class CustomCheckbox extends React.PureComponent {\n    toggle = () => {\n        const {value, onChange} = this.props;\n\n        onChange(!value);\n    };\n\n    render() {\n        const {value} = this.props;\n        const checked = !!value;\n\n        return (\n            <div>\n                <a\n                    className={cx('btn btn-default', {\n                        'btn-success': checked,\n                    })}\n                    onClick={this.toggle}\n                >\n                    {checked ? '已勾选' : '请勾选'}\n                </a>\n                <div className=\"inline m-l-xs\">{checked ? '已勾选' : '请勾选'}</div>\n            </div>\n        );\n    }\n}\n
\n

有了这个代码后,页面配置 form 的 controls 里面就可以通过这样的配置启动了。

\n
{\n    // 其他信息省略了。。\n    type: 'form',\n    controls: [\n        {\n            type: 'custom-checkbox',\n            name: '变量名',\n            label: '自定义组件。'\n        }\n    ]\n}\n
\n

表单项开发主要关心两件事。

\n
    \n
  1. 呈现当前值。如以上例子,勾选了则显示已勾选,否则显示请勾选
  2. \n
  3. 接收用户交互,修改表单项值。如以上例子,当用户点击按钮时,切换当前选中的值。
  4. \n
\n

至于其他功能如:label/description 的展示、表单验证功能、表单布局(常规、左右或者内联)等等,只要是通过 FormItem 注册进去的都无需自己实现。

\n

Renderer

非表单类的组件自定义,主要通过 Renderer 实现。在开始阅读之前,请先阅读 AMis 工作原理

\n
import * as React from 'react';\nimport {Renderer} from 'amis';\n\n@Renderer({\n    test: /(^|\\/)my\\-renderer$/,\n})\nclass CustomRenderer extends React.Component {\n    render() {\n        const {tip, body, render} = this.props;\n\n        return (\n            <div>\n                <p>这是自定义组件:{tip}</p>\n                {body ? (\n                    <div className=\"container\">\n                        {render('body', body, {\n                            // 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个\n                        })}\n                    </div>\n                ) : null}\n            </div>\n        );\n    }\n}\n
\n

这里注册一个 React 组件,当节点的 path 信息是 my-renderer 结尾时,交给当前组件来完成渲染。

\n

请注意 this.props 中的 render 方法,它用来实现容器功能,通过它可以让使用者动态的配置其他渲染模型。

\n

工具

目前主要提供以下工具。

\n

fetch

import {fetch} from 'amis/utils';\n
\n

用来做 ajax 请求。参数说明

\n\n

返回一个 Promise。

\n

如:

\n
import {fetch} from 'amis/utils';\n\nfetch('http://www.baidu.com/api/xxx?a=${a}&b=${b}', {\n    a: 'aa',\n    b: 'bb',\n}).then(function(result) {\n    console.log(result);\n});\n
\n

filter

import {filter} from 'amis/utils';\n
\n

主要用来做字符替换,如:

\n
import {filter} from 'amis/utils';\n\nfilter('blabla?a={a}', {a: 123}); // => 'blabla?a=123'\n
\n", + "toc": { + "label": "目录", + "type": "toc", + "children": [ + { + "label": "FormItem", + "fragment": "formitem", + "fullPath": "#formitem", + "level": 3 }, - }; + { + "label": "Renderer", + "fragment": "renderer", + "fullPath": "#renderer", + "level": 3 + }, + { + "label": "工具", + "fragment": "%E5%B7%A5%E5%85%B7", + "fullPath": "#%E5%B7%A5%E5%85%B7", + "level": 2, + "children": [ + { + "label": "fetch", + "fragment": "fetch", + "fullPath": "#fetch", + "level": 3 + }, + { + "label": "filter", + "fragment": "filter", + "fullPath": "#filter", + "level": 3 + } + ] + } + ], + "level": 0 + } + }; + }); diff --git a/gh-pages/docs/renderers/CRUD-Cards.js b/gh-pages/docs/renderers/CRUD-Cards.js index 30f45df7..5b1d2fd2 100644 --- a/gh-pages/docs/renderers/CRUD-Cards.js +++ b/gh-pages/docs/renderers/CRUD-Cards.js @@ -1,7 +1,7 @@ define('docs/renderers/CRUD-Cards.md', function(require, exports, module) { module.exports = { - "html": "

Cards(CRUD)

请参考Cards

\n
\n", + "html": "

Cards(CRUD)

请参考Cards

\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/CRUD-List.js b/gh-pages/docs/renderers/CRUD-List.js index 9b637509..63bca962 100644 --- a/gh-pages/docs/renderers/CRUD-List.js +++ b/gh-pages/docs/renderers/CRUD-List.js @@ -1,7 +1,7 @@ define('docs/renderers/CRUD-List.md', function(require, exports, module) { module.exports = { - "html": "

List(CRUD)

请参考List

\n
\n", + "html": "

List(CRUD)

请参考List

\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/CRUD-Table.js b/gh-pages/docs/renderers/CRUD-Table.js index 63467df0..e2e8a9b6 100644 --- a/gh-pages/docs/renderers/CRUD-Table.js +++ b/gh-pages/docs/renderers/CRUD-Table.js @@ -1,7 +1,7 @@ define('docs/renderers/CRUD-Table.md', function(require, exports, module) { module.exports = { - "html": "

Table(CRUD)

在 CRUD 中的 Table 主要增加了 Column 里面的以下配置功能,更多参数,请参考Table

\n\n
\n", + "html": "

Table(CRUD)

在 CRUD 中的 Table 主要增加了 Column 里面的以下配置功能,更多参数,请参考Table

\n\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/Cards.js b/gh-pages/docs/renderers/Cards.js index de159b31..b186c2b9 100644 --- a/gh-pages/docs/renderers/Cards.js +++ b/gh-pages/docs/renderers/Cards.js @@ -1,7 +1,7 @@ define('docs/renderers/Cards.md', function(require, exports, module) { module.exports = { - "html": "

Cards

卡片集合。

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"cards" 指定为卡片集合。
titlestring标题
sourcestring${items}数据源, 绑定当前环境变量
placeholderstring‘暂无数据’当没数据的时候的文字提示
classNamestring外层 CSS 类名
headerClassNamestringamis-grid-header顶部外层 CSS 类名
footerClassNamestringamis-grid-footer底部外层 CSS 类名
itemClassNamestringcol-sm-4 col-md-3卡片 CSS 类名
cardCard配置卡片信息
\n
\n", + "html": "

Cards

卡片集合。

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"cards" 指定为卡片集合。
titlestring标题
sourcestring${items}数据源, 绑定当前环境变量
placeholderstring‘暂无数据’当没数据的时候的文字提示
classNamestring外层 CSS 类名
headerClassNamestringamis-grid-header顶部外层 CSS 类名
footerClassNamestringamis-grid-footer底部外层 CSS 类名
itemClassNamestringcol-sm-4 col-md-3卡片 CSS 类名
cardCard配置卡片信息
\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/List.js b/gh-pages/docs/renderers/List.js index 423958de..95ba7ce8 100644 --- a/gh-pages/docs/renderers/List.js +++ b/gh-pages/docs/renderers/List.js @@ -1,7 +1,7 @@ define('docs/renderers/List.md', function(require, exports, module) { module.exports = { - "html": "

List

列表展示。

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"list" 指定为列表展示。
titlestring标题
sourcestring${items}数据源, 绑定当前环境变量
placeholderstring‘暂无数据’当没数据的时候的文字提示
classNamestring外层 CSS 类名
headerClassNamestringamis-list-header顶部外层 CSS 类名
footerClassNamestringamis-list-footer底部外层 CSS 类名
listItemArray配置单条信息
listItem.titlestring标题,支持模板语法如: \\${xxx}
listItem.titleClassNamestringh5标题 CSS 类名
listItem.subTitlestring副标题,支持模板语法如: \\${xxx}
listItem.avatarstring图片地址,支持模板语法如: \\${xxx}
listItem.avatarClassNamestringthumb-sm avatar m-r图片 CSS 类名
listItem.descstring描述,支持模板语法如: \\${xxx}
listItem.bodyArray 或者 Field内容容器,主要用来放置 Field
listItem.actionsArray Of Button按钮区域
\n
\n", + "html": "

List

列表展示。

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"list" 指定为列表展示。
titlestring标题
sourcestring${items}数据源, 绑定当前环境变量
placeholderstring‘暂无数据’当没数据的时候的文字提示
classNamestring外层 CSS 类名
headerClassNamestringamis-list-header顶部外层 CSS 类名
footerClassNamestringamis-list-footer底部外层 CSS 类名
listItemArray配置单条信息
listItem.titlestring标题,支持模板语法如: \\${xxx}
listItem.titleClassNamestringh5标题 CSS 类名
listItem.subTitlestring副标题,支持模板语法如: \\${xxx}
listItem.avatarstring图片地址,支持模板语法如: \\${xxx}
listItem.avatarClassNamestringthumb-sm avatar m-r图片 CSS 类名
listItem.descstring描述,支持模板语法如: \\${xxx}
listItem.bodyArray 或者 Field内容容器,主要用来放置 Field
listItem.actionsArray Of Button按钮区域
\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/Picker.js b/gh-pages/docs/renderers/Picker.js index 5126c464..4d46e3a3 100644 --- a/gh-pages/docs/renderers/Picker.js +++ b/gh-pages/docs/renderers/Picker.js @@ -1,7 +1,7 @@ define('docs/renderers/Picker.md', function(require, exports, module) { module.exports = { - "html": "

Picker

列表选取。可以静态数据,或者通过接口拉取动态数据。

\n\n
\n", + "html": "

Picker

列表选取。可以静态数据,或者通过接口拉取动态数据。

\n\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/Table.js b/gh-pages/docs/renderers/Table.js index 6b21e5d8..d1101692 100644 --- a/gh-pages/docs/renderers/Table.js +++ b/gh-pages/docs/renderers/Table.js @@ -1,7 +1,7 @@ define('docs/renderers/Table.md', function(require, exports, module) { module.exports = { - "html": "

Table

表格展示。

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"table" 指定为 table 渲染器
titlestring标题
sourcestring${items}数据源, 绑定当前环境变量
affixHeaderbooleantrue是否固定表头
columnsTogglableauto 或者 booleanauto展示列显示开关, 自动即:列数量大于或等于 5 个时自动开启
placeholderstring‘暂无数据’当没数据的时候的文字提示
classNamestringpanel-default外层 CSS 类名
tableClassNamestringtable-db table-striped表格 CSS 类名
headerClassNamestringAction.md-table-header顶部外层 CSS 类名
footerClassNamestringAction.md-table-footer底部外层 CSS 类名
toolbarClassNamestringAction.md-table-toolbar工具栏 CSS 类名
columnsArray of Column用来设置列信息
\n
\n", + "html": "

Table

表格展示。

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"table" 指定为 table 渲染器
titlestring标题
sourcestring${items}数据源, 绑定当前环境变量
affixHeaderbooleantrue是否固定表头
columnsTogglableauto 或者 booleanauto展示列显示开关, 自动即:列数量大于或等于 5 个时自动开启
placeholderstring‘暂无数据’当没数据的时候的文字提示
classNamestringpanel-default外层 CSS 类名
tableClassNamestringtable-db table-striped表格 CSS 类名
headerClassNamestringAction.md-table-header顶部外层 CSS 类名
footerClassNamestringAction.md-table-footer底部外层 CSS 类名
toolbarClassNamestringAction.md-table-toolbar工具栏 CSS 类名
columnsArray of Column用来设置列信息
\n
\n", "toc": { "label": "目录", "type": "toc", diff --git a/gh-pages/docs/renderers/Tabs.js b/gh-pages/docs/renderers/Tabs.js new file mode 100644 index 00000000..c8fd1f01 --- /dev/null +++ b/gh-pages/docs/renderers/Tabs.js @@ -0,0 +1,20 @@ +define('docs/renderers/Tabs.md', function(require, exports, module) { + + module.exports = { + "html": "

Tabs

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
属性名类型默认值说明
typestring"tabs"指定为 Tabs 渲染器
classNamestring外层 Dom 的类名
tabsClassNamestringTabs Dom 的类名
tabsArraytabs 内容
tabs[x].titlestringTab 标题
tabs[x].iconiconTab 的图标
tabs[x].tabContainer内容区
tabs[x].hashstring设置以后将跟 url 的 hash 对应
tabs[x].reloadboolean设置以后内容每次都会重新渲染,对于 crud 的重新拉取很有用
tabs[x].classNamestring"bg-white b-l b-r b-b wrapper-md"Tab 区域样式
\n
\n", + "toc": { + "label": "目录", + "type": "toc", + "children": [ + { + "label": "Tabs", + "fragment": "tabs", + "fullPath": "#tabs", + "level": 2 + } + ], + "level": 0 + } + }; + +}); diff --git a/gh-pages/docs/renderers/Types.js b/gh-pages/docs/renderers/Types.js index b90f8227..0276059e 100644 --- a/gh-pages/docs/renderers/Types.js +++ b/gh-pages/docs/renderers/Types.js @@ -1,39 +1,40 @@ define('docs/renderers/Types.md', function(require, exports, module) { - module.exports = { - html: - '

类型说明

Container

Container 不是一个特定的渲染器,而是 AMis 中一个特殊类型,它是以下类型的任何一种。

\n\n

示例:

\n
{\n    "container": "普通一段字符串"\n}\n
\n
{\n    "container": {\n        "type": "button",\n        "label": "按钮"\n    }\n}\n
\n
{\n    "container": [\n        "普通一段字符串",\n\n        {\n            "type": "button",\n            "label": "按钮"\n        },\n\n        ["普通一段字符串", "普通一段字符串"]\n    ]\n}\n
\n

API

Api 类型可以是字符串或者对象。API 中可以直接设置数据发送结构,注意看示例。

\n\n

注意

\n

AMis 所有值为 url 的如: "http://www.baidu.com" 都会被替换成 proxy 代理,如果不希望这么做,请明确指示如: "raw:http://www.baidu.com"。还有为了安全,AMis 默认只能转发公司内部 API 接口,如果您的接口在外网环境,也请明确指示如:"external:http://www.baidu.com"

\n

表达式

配置项中,所有 boolean 类型的配置,都可以用 JS 表达式来配置。所有boolean 配置项,后面加个 On 则是表达式配置方式,可以用 js 语法来根据当前模型中的数据来决定是否启用。\n如:FormItem 中的 disabledOnhiddenOnvisibleOnCRUD 中的 itemDraggableOn 等等。

\n
\n

为了能加入权限控制,表达是中允许可以用 acl.can 方法来检测当前用户是否拥有某个权限。\n如: {"disabledOn": "!can('some-resource')"}。权限能力部分,请前往能力管理,\n权限配置请前往权限配置管理。

\n', - toc: { - label: '目录', - type: 'toc', - children: [ - { - label: '类型说明', - fragment: '%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E', - fullPath: '#%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E', - level: 2, - children: [ - { - label: 'Container', - fragment: 'container', - fullPath: '#container', - level: 3, - }, - { - label: 'API', - fragment: 'api', - fullPath: '#api', - level: 3, - }, - { - label: '表达式', - fragment: '%E8%A1%A8%E8%BE%BE%E5%BC%8F', - fullPath: '#%E8%A1%A8%E8%BE%BE%E5%BC%8F', - level: 3, - }, - ], - }, - ], - level: 0, - }, - }; + + module.exports = { + "html": "

类型说明

Container

Container 不是一个特定的渲染器,而是 AMis 中一个特殊类型,它是以下类型的任何一种。

\n\n

示例:

\n
{\n    \"container\": \"普通一段字符串\"\n}\n
\n
{\n    \"container\": {\n        \"type\": \"button\",\n        \"label\": \"按钮\"\n    }\n}\n
\n
{\n    \"container\": [\n        \"普通一段字符串\",\n\n        {\n            \"type\": \"button\",\n            \"label\": \"按钮\"\n        },\n\n        [\"普通一段字符串\", \"普通一段字符串\"]\n    ]\n}\n
\n

API

Api 类型可以是字符串或者对象。API 中可以直接设置数据发送结构,注意看示例。

\n\n

注意

\n

AMis 所有值为 url 的如: "http://www.baidu.com" 都会被替换成 proxy 代理,如果不希望这么做,请明确指示如: "raw:http://www.baidu.com"。还有为了安全,AMis 默认只能转发公司内部 API 接口,如果您的接口在外网环境,也请明确指示如:"external:http://www.baidu.com"

\n

表达式

配置项中,所有 boolean 类型的配置,都可以用 JS 表达式来配置。所有boolean 配置项,后面加个 On 则是表达式配置方式,可以用 js 语法来根据当前模型中的数据来决定是否启用。\n如:FormItem 中的 disabledOnhiddenOnvisibleOnCRUD 中的 itemDraggableOn 等等。

\n
\n

为了能加入权限控制,表达是中允许可以用 acl.can 方法来检测当前用户是否拥有某个权限。\n如: {"disabledOn": "!can('some-resource')"}。权限能力部分,请前往能力管理,\n权限配置请前往权限配置管理。

\n", + "toc": { + "label": "目录", + "type": "toc", + "children": [ + { + "label": "类型说明", + "fragment": "%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E", + "fullPath": "#%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E", + "level": 2, + "children": [ + { + "label": "Container", + "fragment": "container", + "fullPath": "#container", + "level": 3 + }, + { + "label": "API", + "fragment": "api", + "fullPath": "#api", + "level": 3 + }, + { + "label": "表达式", + "fragment": "%E8%A1%A8%E8%BE%BE%E5%BC%8F", + "fullPath": "#%E8%A1%A8%E8%BE%BE%E5%BC%8F", + "level": 3 + } + ] + } + ], + "level": 0 + } + }; + }); diff --git a/gh-pages/docs/sdk.js b/gh-pages/docs/sdk.js index b7549e7b..bb971863 100644 --- a/gh-pages/docs/sdk.js +++ b/gh-pages/docs/sdk.js @@ -1,32 +1,33 @@ define('docs/sdk.md', function(require, exports, module) { - module.exports = { - title: '如何使用', - html: - '

如何使用

npm i amis\n
\n

安装完后可以在 React Component 这么使用。

\n
import * as React from \'react\';\nimport {\n    render as renderAmis\n} from \'amis\';\n\nclass MyComponent extends React.Component<any, any> {\n    render() {\n        return (\n            <div>\n                <p>通过 AMis 渲染页面</p>\n                {renderAmis({\n                    // schema\n                    // 这里是 AMis 的 Json 配置。\n                    type: \'page\',\n                    title: \'简单页面\',\n                    body: \'内容\'\n                }, {\n                    // props\n                }, {\n                    // env\n                    // 这些是 AMis 需要的一些接口实现\n                    // 可以参考本项目里面的 Demo 部分代码。\n\n                    updateLocation: (location:string/*目标地址*/, replace:boolean/*是replace,还是push?*/) => {\n                        // 用来更新地址栏\n                    },\n\n                    jumpTo: (location:string/*目标地址*/) => {\n                        // 页面跳转, actionType:  link、url 都会进来。\n                    },\n\n                    fetcher: ({\n                        url,\n                        method,\n                        data,\n                        config\n                    }:{\n                        url:string/*目标地址*/,\n                        method:\'get\' | \'post\' | \'put\' | \'delete\'/*发送方式*/,\n                        data: object | void/*数据*/,\n                        config: object/*其他配置*/\n                    }) => {\n                        // 用来发送 Ajax 请求,建议使用 axios\n                    },\n                    notify: (type:\'error\'|\'success\'/**/, msg:string/*提示内容*/) => {\n                        // 用来提示用户\n                    },\n                    alert: (content:string/*提示信息*/) => {\n                        // 另外一种提示,可以直接用系统框\n                    },\n                    confirm: (content:string/*提示信息*/) => {\n                        // 确认框。\n                    }\n                });}\n            </div>\n        );\n    }\n}\n
\n

工作原理

AMis 的渲染过程就是将 json 转成对应的 React 组件。先通过 json 的 type 找到对应的 Component 然后,然后把其他属性作为 props 传递过去完成渲染。

\n

拿一个表单页面来说,如果用React组件调用大概是这样。

\n
<Page\n    title="页面标题"\n    subTitle="副标题"\n>\n    <Form\n        title="用户登录"\n        controls={[\n            {\n                type: \'text\',\n                name: \'username\',\n                label: \'用户名\'\n            }\n        ]}\n    />\n</Page>\n
\n

把以上配置方式换成 AMis JSON, 则是:

\n
{\n  "type": "page",\n  "title": "页面标题",\n  "subTitle": "副标题",\n  "body": {\n    "type": "form",\n    "title": "用户登录",\n    "controls": [\n      {\n        "type": "text",\n        "name": "username",\n        "label": "用户名"\n      }\n    ]\n  }\n}\n
\n

那么,AMis 是如何将 JSON 转成组件的呢?直接根据节点的 type 去跟组件一一对应?似乎很可能会重名比如在表格里面展示的类型 text 跟表单里面的 text是完全不一样的,一个负责展示,一个却负责输入。所以说一个节点要被什么组件渲染,还需要携带上下文(context)信息。

\n

如何去携带上下文(context)信息?AMis 中直接是用节点的路径(path)来作为上下文信息。从上面的例子来看,一共有三个节点,path 信息分别是。

\n\n

根据 path 的信息就能很容易注册组件跟节点对应了。

\n

Page 组件的示例代码

\n
@Renderer({\n    test: /^page$/,\n    // ... 其他信息隐藏了\n})\nexport class PageRenderer extends React.Component {\n    // ... 其他信息隐藏了\n    render() {\n        const {\n            title,\n            body,\n            render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。\n        } = this.props;\n        return (\n            <div className="page">\n                <h1>{title}</h1>\n                <div className="body-container">\n                    {render(\'body\', body)/*渲染孩子节点*/}\n                </div>\n            </div>\n        );\n    }\n}\n
\n

Form 组件的示例代码

\n
@Renderer({\n    test: /(^|\\/)form$/,\n    // ... 其他信息隐藏了\n})\nexport class FormRenderer extends React.Component {\n    // ... 其他信息隐藏了\n    render() {\n        const {\n            title,\n            controls,\n            render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。\n        } = this.props;\n        return (\n            <form className="form">\n                {controls.map((control, index) => (\n                    <div className="form-item" key={index}>\n                        {render(`${index}/control`, control)}\n                    </div>\n                ))}\n            </form>\n        );\n    }\n}\n
\n

Text 组件的示例代码

\n
@Renderer({\n    test: /(^|\\/)form(?:\\/\\d+)?\\/control(?\\/\\d+)?\\/text$/\n    // ... 其他信息隐藏了\n})\nexport class FormItemTextRenderer extends React.Component {\n    // ... 其他信息隐藏了\n    render() {\n        const {\n            label,\n            name,\n            onChange\n        } = this.props;\n        return (\n            <div className="form-group">\n                <label>{label}<label>\n                <input type="text" onChange={(e) => onChange(e.currentTarget.value)} />\n            </div>\n        );\n    }\n}\n
\n

那么渲染过程就是根据节点 path 信息,跟组件池中的组件 test (检测) 信息做匹配,如果命中,则把当前节点转给对应组件渲染,节点中其他属性将作为目标组件的 props。需要注意的是,如果是容器组件,比如以上例子中的 page 组件,从 props 中拿到的 body 是一个子节点,由于节点类型是不固定,由使用者决定,所以不能直接完成渲染,所以交给属性中下发的 render 方法去完成渲染,{render('body', body)},他的工作就是拿子节点的 path 信息去组件池里面找到对应的渲染器,然后交给对应组件去完成渲染。

\n

自定义组件

如果 AMis 中组件不能满足你的需求,同时你又会 React 组件开发,那么就自己定制一个吧。

\n

先来看个简单的例子

\n
import * as React from \'react\';\nimport {\n    Renderer\n} from \'amis\';\n\n@Renderer({\n    test: /(^|\\/)my\\-renderer$/,\n})\nclass CustomRenderer extends React.Component {\n    render() {\n        const  {tip} = this.props;\n        return (\n            <div>这是自定义组件:{tip}</div>\n        );\n    }\n}\n
\n

有了以上这段代码后,就可以这样使用了。

\n
{\n    "type": "page",\n    "title": "自定义组件示例",\n    "body": {\n        "type": "my-renderer",\n        "tip": "简单示例"\n    }\n}\n
\n

如果你看了AMis工作原理应该不难理解,这里注册一个 React 组件,当节点的 path 信息是 my-renderer 结尾时,交给当前组件来完成渲染。\n如果你只写叶子节点的渲染器,已经可以不用看了,如果你的渲染器中有容器需要可以放置其他节点,那么接着看以下这段代码。

\n
import * as React from \'react\';\nimport {\n    Renderer\n} from \'amis\';\n\n@Renderer({\n    test: /(^|\\/)my\\-renderer2$/,\n})\nclass CustomRenderer extends React.Component {\n    render() {\n        const  {\n            tip,\n            body,\n            render\n        } = this.props;\n        return (\n            <div>\n                <p>这是自定义组件:{tip}</p>\n                {body ? (\n                    <div className="container">\n                        {render(\'body\', body, {\n                            // 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个\n                        })}\n                    </div>\n                ) : null}\n            </div>\n        );\n    }\n}\n
\n

有了以上这段代码后,就可以这样使用了。

\n
{\n    "type": "page",\n    "title": "自定义组件示例",\n    "body": {\n        "type": "my-renderer2",\n        "tip": "简单示例",\n        "body": {\n            "type": "form",\n            "controls": [\n                {\n                    "type": "text",\n                    "label": "用户名",\n                    "name": "usename"\n                }\n            ]\n        }\n    }\n}\n
\n

跟第一个列子不同的地方是,这里多了个 render 方法,这个方法就是专门用来渲染子节点的。来看下参数说明:

\n\n

以上是普通渲染器的注册方式,如果是表单项,为了更简单的扩充,请使用 FormItem 注解,而不是 Renderer。 原因是如果用 FormItem 是不用关心:label怎么摆,表单验证器怎么实现,如何适配表单的3中展现方式(水平、上下和内联模式),而只用关心:有了值后如何回显,响应用户交互设置新值。

\n
import * as React from \'react\';\nimport {\n    FormItem\n} from \'amis\';\n\n@FormItem({\n    type: \'custom\'\n})\nclass MyFormItem extends React.Component {\n    render() {\n        const {\n            value,\n            onChange\n        } = this.props;\n\n        return (\n            <div>\n                <p>这个是个自定义组件</p>\n                <p>当前值:{value}</p>\n                <a className="btn btn-default" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>\n            </div>\n        );\n    }\n}\n
\n

有了以上这段代码后,就可以这样使用了。

\n
{\n    "type": "page",\n    "title": "自定义组件示例",\n    "body": {\n            "type": "form",\n            "controls": [\n                {\n                    "type": "text",\n                    "label": "用户名",\n                    "name": "usename"\n                },\n\n                {\n                    "type": "custom",\n                    "label": "随机值",\n                    "name": "randon"\n                }\n            ]\n        }\n}\n
\n
\n

注意: 使用 FormItem 默认是严格模式,即只有必要的属性变化才会重新渲染,有可能满足不了你的需求,如果忽略性能问题,可以传入 strictMode: false 来关闭。

\n
\n

以上的例子都是需要先注册组件,然后再使用的,如果你在自己项目中使用,还有更简单的用法,以下示例直接无需注册。

\n
{\n    "type": "page",\n    "title": "自定义组件示例",\n    "body": {\n            "type": "form",\n            "controls": [\n                {\n                    "type": "text",\n                    "label": "用户名",\n                    "name": "usename"\n                },\n\n                {\n                    "name": "a",\n                    "children": ({\n                        value,\n                        onChange\n                    }) => (\n                        <div>\n                            <p>这个是个自定义组件</p>\n                            <p>当前值:{value}</p>\n                            <a className="btn btn-default" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>\n                        </div>\n                    )\n                }\n            ]\n        }\n}\n
\n

即:通过 children 传递一个React组件,这个示例是一个React Stateless Functional Component,也可以是传统的 React 组件。\n任何节点如果包含 children 这个属性,则都会把当前节点交给 children 来处理,跳过了从 AMis 渲染器池子中选择渲染器的过程。

\n', - toc: { - label: '目录', - type: 'toc', - children: [ - { - label: '如何使用', - fragment: '%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8', - fullPath: '#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8', - level: 2, - }, - { - label: '工作原理', - fragment: '%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86', - fullPath: '#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86', - level: 2, - }, - { - label: '自定义组件', - fragment: '%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6', - fullPath: '#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6', - level: 2, - }, - ], - level: 0, + + module.exports = { + "title": "如何使用", + "html": "

如何使用

npm i amis\n
\n

安装完后可以在 React Component 这么使用。

\n
import * as React from 'react';\nimport {\n    render as renderAmis\n} from 'amis';\n\nclass MyComponent extends React.Component<any, any> {\n    render() {\n        return (\n            <div>\n                <p>通过 AMis 渲染页面</p>\n                {renderAmis({\n                    // schema\n                    // 这里是 AMis 的 Json 配置。\n                    type: 'page',\n                    title: '简单页面',\n                    body: '内容'\n                }, {\n                    // props\n                }, {\n                    // env\n                    // 这些是 AMis 需要的一些接口实现\n                    // 可以参考本项目里面的 Demo 部分代码。\n\n                    updateLocation: (location:string/*目标地址*/, replace:boolean/*是replace,还是push?*/) => {\n                        // 用来更新地址栏\n                    },\n\n                    jumpTo: (location:string/*目标地址*/) => {\n                        // 页面跳转, actionType:  link、url 都会进来。\n                    },\n\n                    fetcher: ({\n                        url,\n                        method,\n                        data,\n                        config\n                    }:{\n                        url:string/*目标地址*/,\n                        method:'get' | 'post' | 'put' | 'delete'/*发送方式*/,\n                        data: object | void/*数据*/,\n                        config: object/*其他配置*/\n                    }) => {\n                        // 用来发送 Ajax 请求,建议使用 axios\n                    },\n                    notify: (type:'error'|'success'/**/, msg:string/*提示内容*/) => {\n                        // 用来提示用户\n                    },\n                    alert: (content:string/*提示信息*/) => {\n                        // 另外一种提示,可以直接用系统框\n                    },\n                    confirm: (content:string/*提示信息*/) => {\n                        // 确认框。\n                    }\n                });}\n            </div>\n        );\n    }\n}\n
\n

工作原理

AMis 的渲染过程就是将 json 转成对应的 React 组件。先通过 json 的 type 找到对应的 Component 然后,然后把其他属性作为 props 传递过去完成渲染。

\n

拿一个表单页面来说,如果用React组件调用大概是这样。

\n
<Page\n    title=\"页面标题\"\n    subTitle=\"副标题\"\n>\n    <Form\n        title=\"用户登录\"\n        controls={[\n            {\n                type: 'text',\n                name: 'username',\n                label: '用户名'\n            }\n        ]}\n    />\n</Page>\n
\n

把以上配置方式换成 AMis JSON, 则是:

\n
{\n  \"type\": \"page\",\n  \"title\": \"页面标题\",\n  \"subTitle\": \"副标题\",\n  \"body\": {\n    \"type\": \"form\",\n    \"title\": \"用户登录\",\n    \"controls\": [\n      {\n        \"type\": \"text\",\n        \"name\": \"username\",\n        \"label\": \"用户名\"\n      }\n    ]\n  }\n}\n
\n

那么,AMis 是如何将 JSON 转成组件的呢?直接根据节点的 type 去跟组件一一对应?似乎很可能会重名比如在表格里面展示的类型 text 跟表单里面的 text是完全不一样的,一个负责展示,一个却负责输入。所以说一个节点要被什么组件渲染,还需要携带上下文(context)信息。

\n

如何去携带上下文(context)信息?AMis 中直接是用节点的路径(path)来作为上下文信息。从上面的例子来看,一共有三个节点,path 信息分别是。

\n\n

根据 path 的信息就能很容易注册组件跟节点对应了。

\n

Page 组件的示例代码

\n
@Renderer({\n    test: /^page$/,\n    // ... 其他信息隐藏了\n})\nexport class PageRenderer extends React.Component {\n    // ... 其他信息隐藏了\n    render() {\n        const {\n            title,\n            body,\n            render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。\n        } = this.props;\n        return (\n            <div className=\"page\">\n                <h1>{title}</h1>\n                <div className=\"body-container\">\n                    {render('body', body)/*渲染孩子节点*/}\n                </div>\n            </div>\n        );\n    }\n}\n
\n

Form 组件的示例代码

\n
@Renderer({\n    test: /(^|\\/)form$/,\n    // ... 其他信息隐藏了\n})\nexport class FormRenderer extends React.Component {\n    // ... 其他信息隐藏了\n    render() {\n        const {\n            title,\n            controls,\n            render // 用来渲染孩子节点,如果当前是叶子节点则可以忽略。\n        } = this.props;\n        return (\n            <form className=\"form\">\n                {controls.map((control, index) => (\n                    <div className=\"form-item\" key={index}>\n                        {render(`${index}/control`, control)}\n                    </div>\n                ))}\n            </form>\n        );\n    }\n}\n
\n

Text 组件的示例代码

\n
@Renderer({\n    test: /(^|\\/)form(?:\\/\\d+)?\\/control(?\\/\\d+)?\\/text$/\n    // ... 其他信息隐藏了\n})\nexport class FormItemTextRenderer extends React.Component {\n    // ... 其他信息隐藏了\n    render() {\n        const {\n            label,\n            name,\n            onChange\n        } = this.props;\n        return (\n            <div className=\"form-group\">\n                <label>{label}<label>\n                <input type=\"text\" onChange={(e) => onChange(e.currentTarget.value)} />\n            </div>\n        );\n    }\n}\n
\n

那么渲染过程就是根据节点 path 信息,跟组件池中的组件 test (检测) 信息做匹配,如果命中,则把当前节点转给对应组件渲染,节点中其他属性将作为目标组件的 props。需要注意的是,如果是容器组件,比如以上例子中的 page 组件,从 props 中拿到的 body 是一个子节点,由于节点类型是不固定,由使用者决定,所以不能直接完成渲染,所以交给属性中下发的 render 方法去完成渲染,{render('body', body)},他的工作就是拿子节点的 path 信息去组件池里面找到对应的渲染器,然后交给对应组件去完成渲染。

\n

自定义组件

如果 AMis 中组件不能满足你的需求,同时你又会 React 组件开发,那么就自己定制一个吧。

\n

先来看个简单的例子

\n
import * as React from 'react';\nimport {\n    Renderer\n} from 'amis';\n\n@Renderer({\n    test: /(^|\\/)my\\-renderer$/,\n})\nclass CustomRenderer extends React.Component {\n    render() {\n        const  {tip} = this.props;\n        return (\n            <div>这是自定义组件:{tip}</div>\n        );\n    }\n}\n
\n

有了以上这段代码后,就可以这样使用了。

\n
{\n    \"type\": \"page\",\n    \"title\": \"自定义组件示例\",\n    \"body\": {\n        \"type\": \"my-renderer\",\n        \"tip\": \"简单示例\"\n    }\n}\n
\n

如果你看了AMis工作原理应该不难理解,这里注册一个 React 组件,当节点的 path 信息是 my-renderer 结尾时,交给当前组件来完成渲染。\n如果你只写叶子节点的渲染器,已经可以不用看了,如果你的渲染器中有容器需要可以放置其他节点,那么接着看以下这段代码。

\n
import * as React from 'react';\nimport {\n    Renderer\n} from 'amis';\n\n@Renderer({\n    test: /(^|\\/)my\\-renderer2$/,\n})\nclass CustomRenderer extends React.Component {\n    render() {\n        const  {\n            tip,\n            body,\n            render\n        } = this.props;\n        return (\n            <div>\n                <p>这是自定义组件:{tip}</p>\n                {body ? (\n                    <div className=\"container\">\n                        {render('body', body, {\n                            // 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个\n                        })}\n                    </div>\n                ) : null}\n            </div>\n        );\n    }\n}\n
\n

有了以上这段代码后,就可以这样使用了。

\n
{\n    \"type\": \"page\",\n    \"title\": \"自定义组件示例\",\n    \"body\": {\n        \"type\": \"my-renderer2\",\n        \"tip\": \"简单示例\",\n        \"body\": {\n            \"type\": \"form\",\n            \"controls\": [\n                {\n                    \"type\": \"text\",\n                    \"label\": \"用户名\",\n                    \"name\": \"usename\"\n                }\n            ]\n        }\n    }\n}\n
\n

跟第一个列子不同的地方是,这里多了个 render 方法,这个方法就是专门用来渲染子节点的。来看下参数说明:

\n\n

以上是普通渲染器的注册方式,如果是表单项,为了更简单的扩充,请使用 FormItem 注解,而不是 Renderer。 原因是如果用 FormItem 是不用关心:label怎么摆,表单验证器怎么实现,如何适配表单的3中展现方式(水平、上下和内联模式),而只用关心:有了值后如何回显,响应用户交互设置新值。

\n
import * as React from 'react';\nimport {\n    FormItem\n} from 'amis';\n\n@FormItem({\n    type: 'custom'\n})\nclass MyFormItem extends React.Component {\n    render() {\n        const {\n            value,\n            onChange\n        } = this.props;\n\n        return (\n            <div>\n                <p>这个是个自定义组件</p>\n                <p>当前值:{value}</p>\n                <a className=\"btn btn-default\" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>\n            </div>\n        );\n    }\n}\n
\n

有了以上这段代码后,就可以这样使用了。

\n
{\n    \"type\": \"page\",\n    \"title\": \"自定义组件示例\",\n    \"body\": {\n            \"type\": \"form\",\n            \"controls\": [\n                {\n                    \"type\": \"text\",\n                    \"label\": \"用户名\",\n                    \"name\": \"usename\"\n                },\n\n                {\n                    \"type\": \"custom\",\n                    \"label\": \"随机值\",\n                    \"name\": \"randon\"\n                }\n            ]\n        }\n}\n
\n
\n

注意: 使用 FormItem 默认是严格模式,即只有必要的属性变化才会重新渲染,有可能满足不了你的需求,如果忽略性能问题,可以传入 strictMode: false 来关闭。

\n
\n

以上的例子都是需要先注册组件,然后再使用的,如果你在自己项目中使用,还有更简单的用法,以下示例直接无需注册。

\n
{\n    \"type\": \"page\",\n    \"title\": \"自定义组件示例\",\n    \"body\": {\n            \"type\": \"form\",\n            \"controls\": [\n                {\n                    \"type\": \"text\",\n                    \"label\": \"用户名\",\n                    \"name\": \"usename\"\n                },\n\n                {\n                    \"name\": \"a\",\n                    \"children\": ({\n                        value,\n                        onChange\n                    }) => (\n                        <div>\n                            <p>这个是个自定义组件</p>\n                            <p>当前值:{value}</p>\n                            <a className=\"btn btn-default\" onClick={() => onChange(Math.round(Math.random() * 10000))}>随机修改</a>\n                        </div>\n                    )\n                }\n            ]\n        }\n}\n
\n

即:通过 children 传递一个React组件,这个示例是一个React Stateless Functional Component,也可以是传统的 React 组件。\n任何节点如果包含 children 这个属性,则都会把当前节点交给 children 来处理,跳过了从 AMis 渲染器池子中选择渲染器的过程。

\n", + "toc": { + "label": "目录", + "type": "toc", + "children": [ + { + "label": "如何使用", + "fragment": "%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8", + "fullPath": "#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8", + "level": 2 }, - }; + { + "label": "工作原理", + "fragment": "%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86", + "fullPath": "#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86", + "level": 2 + }, + { + "label": "自定义组件", + "fragment": "%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6", + "fullPath": "#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6", + "level": 2 + } + ], + "level": 0 + } + }; + }); diff --git a/gh-pages/index.html b/gh-pages/index.html index f89b740c..f0baf2a3 100644 --- a/gh-pages/index.html +++ b/gh-pages/index.html @@ -21,8 +21,8 @@
- - + +