除了使用爱速搭内置的组件以外,还可以通过创建自定义组价,扩充可使用的组件

前提条件

  1. 需要对 amis 整体概念有一定的了解,可以查看 这里
  2. 需要有一定的 JavaScript 基础
  3. 至少能够熟练使用 ReactVuejQuery 中任意一门框架

基本流程

现在说明一下如何创建和使用一个自定义组件

组件创建

进入自定义组件模块,点击左上角「新增组件」按钮,填写如下组件配置:

image.png

  • 组件名称:设置该自定义组件的名称
  • 组件类型:选择新建组件的类型,主要有「展示类」、「普通表单项」、「选择器表单项」、「其他」4 类,后面文档会详细介绍
  • 组件 key:在 amis schema 配置中的 type 值
  • 框架:可选 React、Vue 和 jQuery 作为开发框架
  • 是否启用:可以选择组件是否启用
  • 说明:组件的说明,描述组件

如上图填写完组件配置后,点击确认,进入组件编辑界面

组件代码编辑

自定义组件代码核心是用 export default 来导出编写好的自定义组件,如下:

  1. import React from 'react';
  2. export default class Test extends React.Component {
  3. render() {
  4. return <div>这是一个 React 自定义组件</div>;
  5. }
  6. }

调试

组件保存后会自动进入构建状态,调试按钮将禁用,当构建成功后,即可点击调试按钮,进行组件调试操作:

image.png

调试面板左侧为组件渲染后效果,右侧为 amis schema,可以调整右侧 schema,动态调试组件效果。

例如我们修改代码如下:

image.png

我们获取 props 中的 tip 属性,然后我们在调试面板右侧,修改 amis schema,添加 tip 属性,可以发现组件渲染出该文本

修改完代码后,需要保存并构建完成后,才可以看到最新的组件效果

组件使用

在 amis schema 中,可以通过 type 来使用自定义组件,例如上例中我们新建的组件 key 为 custom-hello-wolrd,且是普通展示类,则可以通过 "type": "custom-hello-wolrd" 使用该自定义组件

我们新建一个测试页面,并编辑页面 schema 如下:

  1. {
  2. "type": "page",
  3. "body": {
  4. "type": "custom-hello-world",
  5. "tip": "是不是很棒!"
  6. }
  7. }

image.png

可以看到上图中自定义组件已经可以正常使用了。

如何让接口支持代理

为了让接口支持代理,需要将地址写在配置中,比如自定义组件代码是是

  1. import React from 'react';
  2. export default class Test extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {result: 'loading'};
  6. this.load();
  7. }
  8. async load() {
  9. const response = await fetch(this.props.api);
  10. const result = await response.text();
  11. this.setState({result});
  12. }
  13. render() {
  14. return <div>{this.state.result}</div>;
  15. }
  16. }

然后在页面配置里的组件属性里加上 api 地址,比如

  1. {
  2. "type": "page",
  3. "body": {
  4. "type": "custom-hello-world",
  5. "api": "https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/mock2/page/initData"
  6. }
  7. }

在实际运行时,这个 api 属性会自动转成代理地址

添加 npm 依赖

平台支持引入在线第三方 npm 库进行辅助开发

首先点击右上角依赖管理,搜索并添加最新版本的 day.js,该插件已内置安装,因此可以直接使用:

image.png

我们使用展示类,并选中 React 作为开发框架,下面我们引用 day.js 并打印一下当前时间,代码如下:

  1. import React from 'react';
  2. import dayjs from 'dayjs';
  3. export default class Test extends React.Component {
  4. render() {
  5. return <div>现在时间是:{dayjs().format('YYYY-DD-MM HH:mm:ss')}</div>;
  6. }
  7. }

效果如下:

image.png

内置已安装的插件,都可以直接使用

开发框架

平台支持 React、Vue、jQuery 作为组件的开发框架

React(推荐)

平台以及 amis 本身是基于 React 开发的,因此使用 React 可以更好地整合开发组件

导出组件类

核心方式是通过 export default 来导出组件类,从而使组件生效

  1. import React from 'react';
  2. export default class Test extends React.Component {
  3. render() {
  4. return <div>这是一个 React 自定义组件</div>;
  5. }
  6. }

使用 UI 组件库

可以使用 npm UI 组件库,例如 Ant Design

首先依赖管理中添加 antd 组件库,然后选择展示类,和 React 作为开发框架,然后编写代码如下:

  1. import React from 'react';
  2. import {Button} from 'antd';
  3. // 引用 antd 样式
  4. import 'antd/dist/antd.css';
  5. export default class Test extends React.Component {
  6. render() {
  7. return (
  8. <div>
  9. <Button type="primary">Primary Button</Button>
  10. <Button>Default Button</Button>
  11. <Button type="dashed">Dashed Button</Button>
  12. <br />
  13. <Button type="text">Text Button</Button>
  14. <Button type="link">Link Button</Button>
  15. </div>
  16. );
  17. }
  18. }

效果如下:

image.png

上例中我们展示了 antd 的 Button 组件

Vue 2

我们下面开发一个简单的自定义组价,打印一行文本:

  1. export default {
  2. template: '<div>这是一个 {{name}} 自定义组件</div>',
  3. data: {
  4. name: 'Vue'
  5. },
  6. methods: {
  7. foo() {
  8. console.log('foo');
  9. }
  10. },
  11. created() {
  12. this.foo();
  13. }
  14. };

使用第三方 UI 组件库

可以使用第三方组件库,例如 Element-UI

首先依赖管理里添加 element-ui 组件库,然后新建组件,选择展示类组件,勾选 Vue 作为开发框架,代码如下:

  1. import {Button} from 'element-ui';
  2. // 引入 element-ui的样式
  3. import 'element-ui/lib/theme-chalk/index.css';
  4. export default class Test {
  5. template = `
  6. <div>
  7. <el-button>默认按钮</el-button>
  8. <el-button type="primary">主要按钮</el-button>
  9. <el-button type="success">成功按钮</el-button>
  10. <el-button type="info">信息按钮</el-button>
  11. <el-button type="warning">警告按钮</el-button>
  12. <el-button type="danger">危险按钮</el-button>
  13. </div>
  14. `;
  15. components = {
  16. 'el-button': Button
  17. };
  18. }

效果如下:

image.png

Vue 3

请注意,在一个应用中不能混用 Vue 2 和 Vue 3 组件,因为 vue 只有一个 package 名称,无法同时存在多个版本

Vue 3 在接口层面做了很大改动,它的使用方式和 Vue 2 不同。

默认展现的示例是:

  1. export default ({createApp, props, funcs}) => {
  2. const app = createApp({
  3. template: `
  4. <div id="event-handling">
  5. <p>{{ message }}</p>
  6. <button v-on:click="reverseMessage">反转 Message</button>
  7. </div>
  8. `,
  9. data() {
  10. return {
  11. message: 'Hello Vue.js!'
  12. };
  13. },
  14. methods: {
  15. reverseMessage() {
  16. this.message = this.message.split('').reverse().join('');
  17. }
  18. }
  19. });
  20. return app;
  21. };

爱速搭会传递 createApp 方法,而插件需要返回构建好的 app 对象。

使用第三方 UI 库

首先在依赖中添加,然后通过 app.use 方法来添加这个第三方 UI 库的插件

  1. import './style.scss'; // 自定义样式,文件名称不可修改
  2. import ElementPlus from 'element-plus';
  3. import 'element-plus/dist/index.css';
  4. export default ({createApp, props, funcs}) => {
  5. const app = createApp({
  6. template: `
  7. <el-row>
  8. <el-button>默认按钮</el-button>
  9. <el-button type="primary">主要按钮</el-button>
  10. <el-button type="success">成功按钮</el-button>
  11. <el-button type="info">信息按钮</el-button>
  12. <el-button type="warning">警告按钮</el-button>
  13. <el-button type="danger">危险按钮</el-button>
  14. </el-row>
  15. `
  16. });
  17. app.use(ElementPlus);
  18. return app;
  19. };

jQuery

平台内部支持用 jQuery 进行开发,且进行了一层简单的封装,方便用户更好的操作。下面我们创建一个简单的自定义组件,打印一行文本:

  1. import $ from 'jquery';
  2. export default {
  3. template: `这是个`,
  4. /**
  5. * 组件挂载的时候调用
  6. */
  7. onMount(props) {
  8. this.foo();
  9. },
  10. /**
  11. * amis props 更新的时候调用
  12. */
  13. onUpdate(props, prevProps) {},
  14. /**
  15. * 组件销货的时候调用
  16. */
  17. onUnmout(props) {},
  18. foo() {
  19. // this.$root 获取当前顶级dom
  20. $(this.$root).append(' jQuery 自定义组件');
  21. }
  22. };

效果如下:

image.png

获取 schema 配置的属性

可以在 schema 中配置属性,然后在组件中获取,例如:

React 中:

  1. import React from 'react';
  2. export default class Test extends React.Component {
  3. render() {
  4. const tip = this.props.tip;
  5. return <div>提示文本: {tip}</div>;
  6. }
  7. }

使用组件时可以配置 schema 如下:

  1. {
  2. "type": "xxx", // 组件的 key
  3. "tip": "这是一段提示"
  4. }

Vue 中:

  1. export default {
  2. template: '<div>提示文本:{{tip}}</div>',
  3. data: {
  4. tip: '' // 需要声明一个空的值,否则可能会报错
  5. }
  6. };

由于该 Vue 特性,需要在使用前声明一个空的默认值

效果如下:

image.png

jQuery 中

  1. import $ from 'jquery';
  2. export default {
  3. template: `提示文本:<span id="tip"></span>`,
  4. onMount(props) {
  5. $('#tip').text(props.tip);
  6. },
  7. onUpdate(props) {
  8. $('#tip').text(props.tip);
  9. }
  10. };

效果如下:

image.png

render

渲染器方法,可以在自定义组件中渲染已有的 amis 组件

适用条件

  • 开发框架:React
  • 组件类型:展示类表单项选择器表单项

函数签名

  1. (region, node, subProps) => JSX.Element;
  • region:给当前组件设置一个 key
  • node:amis 组件的配置项
  • subProps:可以不填,额外的一些配置项

使用方法

React 中:

  1. import React from 'react';
  2. export default class Test extends React.Component {
  3. render() {
  4. const {render} = this.props;
  5. return (
  6. <div>
  7. 这是一个 React 自定义组件,
  8. {render('test', {
  9. type: 'button',
  10. label: '这是amis按钮',
  11. actionType: 'dialog',
  12. dialog: {
  13. title: '弹框',
  14. body: '这是一个弹框'
  15. }
  16. })}
  17. </div>
  18. );
  19. }
  20. }

效果如下:

image.png

image.png

onAction

调用 amis 内置的行为,可参考 行为

适用条件

  • 开发框架:React、Vue、jQuery
  • 组件类型:展示类、表单项、选择器表单项

函数签名

  1. (event, action, ctx) => void;
  • event:可忽略,传入 null 即可;
  • action:传入需要执行的行为对象配置,参考 行为
  • ctx:给当前行为内传入一些数据。如果没有则传入空对象{},否则会报错

使用方法

我们来通过该方法实现:点击按钮,然后调起一个 amis 弹框。

React 中
  1. import React from 'react';
  2. export default class Test extends React.Component {
  3. constructor() {
  4. super();
  5. this.handleClick = this.handleClick.bind(this);
  6. }
  7. handleClick() {
  8. const onAction = this.props.onAction;
  9. onAction(
  10. null,
  11. {
  12. actionType: 'dialog',
  13. dialog: {
  14. title: '弹框',
  15. body: '这是一个amis弹框'
  16. }
  17. },
  18. {}
  19. );
  20. }
  21. render() {
  22. return <button onClick={this.handleClick}>调起 amis 弹框</button>;
  23. }
  24. }
Vue 中
  1. export default {
  2. template: '<button v-on:click="handleClick">调起 amis 弹框</button>',
  3. data: {},
  4. methods: {
  5. handleClick() {
  6. this.$emit('onAction', [
  7. null,
  8. {
  9. actionType: 'dialog',
  10. dialog: {
  11. title: '弹框',
  12. body: '这是一个amis弹框'
  13. }
  14. },
  15. {}
  16. ]);
  17. }
  18. }
  19. };
jQuery 中
  1. import $ from 'jquery';
  2. export default {
  3. template: `<button id="btn">调起 amis 弹框</button>`,
  4. onMount(props) {
  5. $('#btn').click(() => {
  6. props.onAction(
  7. null,
  8. {
  9. actionType: 'dialog',
  10. dialog: {
  11. title: '弹框',
  12. body: '这是一个amis弹框'
  13. }
  14. },
  15. {}
  16. );
  17. });
  18. }
  19. };

value 和 onChange

当编写表单项类型的自定义组件时,最重要的是与 Form 数据域的通信,而实现该通信的核心就是 value 属性和 onChange 方法

适用条件

  • 开发框架:React、Vue、jQuery
  • 组件类型:普通表单项、选择器表单项

使用方法

核心思路是:

  • 拿到 props 中的 value 属性,并赋值给自定义组件内输入框
  • 监听自定义组件内输入框 change 事件或 value 值变化,如果有变化,通过 onChange 事件,将新的 value 值同步给 amis 层

下面来演示不同框架下的使用示例:

React 中
  1. import React from 'react';
  2. export default class Test extends React.PureComponent {
  3. constructor() {
  4. this.handleChange = this.handleChange.bind(this);
  5. }
  6. // 监听 input 的 change 事件,并同步value值
  7. handleChange(event) {
  8. const onChange = this.props.onChange;
  9. // 调用amis onChange方法,同步最新的value值
  10. onChange(event.target.value);
  11. }
  12. render() {
  13. // 获取 props 中的 value 属性,并赋值给input
  14. const {value} = this.props;
  15. return <input type="text" value={value} onChange={this.handleChange} />;
  16. }
  17. }

效果如下:

image.png

更改 input 值,可以观察表单数据域的变化。

Vue 中
  1. export default {
  2. // 获取 value 属性并绑定给 input 输入框
  3. template: `<input type="text" v-model="value" />`,
  4. watch: {
  5. value: function (newValue, oldValue) {
  6. // 通过 $emit 调用 amis 的 onChange 事件,同步 value 值
  7. this.$emit('onChange', newValue);
  8. }
  9. }
  10. };

vue 中可以利用 watch 监听器来实现 value 同步逻辑。

效果如下;

image.png

更改 input 值,可以观察表单数据域的变化。

jQuery 中
  1. import $ from 'jquery';
  2. export default {
  3. template: `<input type="text" id="input" />`,
  4. onMount(props) {
  5. // 获取 props 中 value 属性
  6. $('#input').attr('value', props.value);
  7. // 给输入框绑定 input 监听事件,同步 value 值
  8. $('#input').on('input', function (e) {
  9. // 调用amis的 onChange 方法
  10. props.onChange(e.target.value);
  11. });
  12. }
  13. };

效果如下:

image.png

更改 input 值,可以观察表单数据域的变化。

onBulkChange

该方法类似于 onChange,不同点在于,它可以批量修改表单项的值。

  1. import React from 'react';
  2. export default class Test extends React.PureComponent {
  3. constructor() {
  4. this.handleChange = this.handleChange.bind(this);
  5. }
  6. handleChange(event) {
  7. const {name, other, onBulkChange} = this.props;
  8. const value = event.target.value;
  9. // 调用amis onChange方法,变更表单项值
  10. onBulkChange({
  11. [name]: value,
  12. [other]: value
  13. });
  14. }
  15. render() {
  16. // 获取表单项 value 属性
  17. const {value} = this.props;
  18. return <input type="text" value={value} onChange={this.handleChange} />;
  19. }
  20. }

例如上例中,我们实现一个:修改当前输入框,会同步修改另外一个数据框的值,具体 schema 如下

  1. {
  2. "type": "form",
  3. "mode": "horizontal",
  4. "debug": true,
  5. "controls": [
  6. {
  7. "type": "custom-test",
  8. "name": "text1",
  9. "label": "text1",
  10. "other": "text2"
  11. },
  12. {
  13. "type": "text",
  14. "name": "text1",
  15. "label": "test2"
  16. }
  17. ]
  18. }

text1 变化时,获取 other 属性,该属性配置的是要同步修改的另外一个数据框的 name 值,即 text2,然后调用 onBulkChange,批量修改数据域值

效果如下:

image.png

vue 和 jquery 使用逻辑相同,具体使用语法方法见 onChange

options

选择器表单项特有的属性,一个选择器组件总有一组选项,可以提供给用户勾选一项或多项,默认该属性为空数组

适用范围

  • 开发框架:React、Vue、jQuery
  • 组件类型:选择器表单项

手动配置

你可以手动在 schema 中配置 options 吗,然后在组件内获取并使用,例如:

  1. import React from 'react';
  2. export default class Test extends React.PureComponent {
  3. constructor() {
  4. this.handleChange = this.handleChange.bind(this);
  5. }
  6. handleChange(event) {
  7. // 调用amis onToggle 方法,变更选择器表单项值
  8. const {onToggle, options} = this.props;
  9. const option = options.find(o => o.value === event.target.value);
  10. onToggle(option);
  11. }
  12. render() {
  13. // 获取表单项 value 和 options 属性
  14. const {value, options} = this.props;
  15. return (
  16. <select value={value} onChange={this.handleChange}>
  17. {options.map(option => (
  18. <option key={option.value} value={option.value}>
  19. {option.label}
  20. </option>
  21. ))}
  22. </select>
  23. );
  24. }
  25. }

编辑 schema 如下:

  1. {
  2. "type": "form",
  3. "mode": "horizontal",
  4. "debug": true,
  5. "controls": [
  6. {
  7. "type": "custom-test",
  8. "name": "test",
  9. "label": "test",
  10. "options": [
  11. {
  12. "label": "Option A",
  13. "value": "a"
  14. },
  15. {
  16. "label": "Option B",
  17. "value": "b"
  18. },
  19. {
  20. "label": "Option C",
  21. "value": "c"
  22. }
  23. ]
  24. }
  25. ]
  26. }

这种方式和普通获取 schema 属性无区别。

动态加载

选择器表单项可以通过配置 source 属性,来动态拉取远程选项,然后在组件更新钩子函数中,获取拉取到的新的 options

setOptions

可以手动调用该方法动态设置 options

适用范围

  • 开发框架:React、Vue、jQuery
  • 组件类型:选择器表单项

函数签名

  1. (options) => void;
  • options:一个对象数组,里面有若干选项

使用方法

  1. import React from 'react';
  2. export default class Test extends React.PureComponent {
  3. constructor() {
  4. this.handleChange = this.handleChange.bind(this);
  5. }
  6. // 组件挂载时,调用 setOptions,设置 options
  7. componentDidMount() {
  8. const {setOptions} = this.props;
  9. setOptions([
  10. {
  11. label: 'Option A',
  12. value: 'a'
  13. },
  14. {
  15. label: 'Option B',
  16. value: 'b'
  17. },
  18. {
  19. label: 'Option C',
  20. value: 'c'
  21. }
  22. ]);
  23. }
  24. handleChange(event) {
  25. // 调用amis onToggle 方法,变更选择器表单项值
  26. const {onToggle, options} = this.props;
  27. const option = options.find(o => o.value === event.target.value);
  28. onToggle(option);
  29. }
  30. render() {
  31. // 获取表单项 value 和 options 属性
  32. const {value, options} = this.props;
  33. return (
  34. <select value={value} onChange={this.handleChange}>
  35. {options.map(option => (
  36. <option key={option.value} value={option.value}>
  37. {option.label}
  38. </option>
  39. ))}
  40. </select>
  41. );
  42. }
  43. }

selectedOptions

由于 拼接符-delimiter拼接值-joinvalues提取多选值-extractvalue 的存在,value 值格式可以是多种格式。

selectedOptions 则永远为当前选中的值的数组形式,方便逻辑操作。

适用范围

  • 开发框架:React、Vue、jQuery
  • 组件类型:选择器表单项

onToggle

选择器表单项专用属性,类似于 onChange,不同的是,该方法需要传入一个完整的选项对象,可以设置选项切换勾选。

适用范围

  • 开发框架:React、Vue、jQuery
  • 组件类型:选择器表单项

函数签名

  1. (option) => void;
  • option:一个选项对象

使用方法

  1. import React from 'react';
  2. export default class Test extends React.PureComponent {
  3. constructor() {
  4. this.handleChange = this.handleChange.bind(this);
  5. }
  6. handleChange(event) {
  7. // 调用amis onToggle 方法,变更选择器表单项值
  8. const {onToggle, options} = this.props;
  9. const option = options.find(o => o.value === event.target.value);
  10. onToggle(option);
  11. }
  12. render() {
  13. // 获取表单项 value 和 options 属性
  14. const {value, options} = this.props;
  15. return (
  16. <select value={value} onChange={this.handleChange}>
  17. {options.map(option => (
  18. <option key={option.value} value={option.value}>
  19. {option.label}
  20. </option>
  21. ))}
  22. </select>
  23. );
  24. }
  25. }

与 onChange 有什么不同

例如有如下 options:

  1. {
  2. "options": [
  3. {
  4. "label": "Option A",
  5. "value": "a"
  6. },
  7. {
  8. "label": "Option B",
  9. "value": "b"
  10. },
  11. {
  12. "label": "Option B",
  13. "value": "b"
  14. }
  15. ]
  16. }
  • 例如使用 onChange,当第一次勾选了 Option A 后,调用 onChange,同步表单项 value 值为"a",当用户再次点击 Option A,该表单项的值仍然是 "a",因为又一次重复设置了 value;
  • 而使用 onToggle,当第一次勾选了 Option A 后,用户再次点击 Option A后,将会取消勾选 Option A 选项,value 值将为空字符串""

在配置 multiple:true,选择器支持多选时:

第一次选中 Option A, value 值为 "a",点击 Option C 时,value 会变成 "a,c",再次点击 Option C ,则 value 会变为"a"

onToggleAll

切换全选和全不选

适用范围

  • 开发框架:React、Vue、jQuery
  • 组件类型:选择器表单项
  • 表达项 schema 需配置 multiple: true

使用方法

  1. import React from 'react';
  2. export default class Test extends React.PureComponent {
  3. constructor() {
  4. this.handleChange = this.handleChange.bind(this);
  5. this.handleSelectAll = this.handleSelectAll.bind(this);
  6. }
  7. handleChange(event) {
  8. // 调用amis onToggle 方法,变更选择器表单项值
  9. const {onToggle, options} = this.props;
  10. const option = options.find(o => o.value === event.target.value);
  11. onToggle(option);
  12. }
  13. handleSelectAll() {
  14. const {onToggleAll} = this.props;
  15. onToggleAll();
  16. }
  17. render() {
  18. // 获取表单项 value 和 options 属性
  19. const {value, options} = this.props;
  20. return (
  21. <>
  22. <select value={value} onChange={this.handleChange}>
  23. {options.map(option => (
  24. <option key={option.value} value={option.value}>
  25. {option.label}
  26. </option>
  27. ))}
  28. </select>
  29. <button onClick={this.handleSelectAll}>全选</button>
  30. </>
  31. );
  32. }
  33. }

效果如下:

image.png