自定义组件
除了使用爱速搭内置的组件以外,还可以通过创建自定义组价,扩充可使用的组件
前提条件
基本流程
现在说明一下如何创建和使用一个自定义组件
组件创建
进入自定义组件模块,点击左上角「新增组件」按钮,填写如下组件配置:
- 组件名称:设置该自定义组件的名称
- 组件类型:选择新建组件的类型,主要有「展示类」、「普通表单项」、「选择器表单项」、「其他」4 类,后面文档会详细介绍
- 组件 key:在 amis schema 配置中的 type 值
- 框架:可选 React、Vue 和 jQuery 作为开发框架
- 是否启用:可以选择组件是否启用
- 说明:组件的说明,描述组件
如上图填写完组件配置后,点击确认,进入组件编辑界面
组件代码编辑
自定义组件代码核心是用 export default
来导出编写好的自定义组件,如下:
1import React from 'react';
2
3export default class Test extends React.Component {
4 render() {
5 return <div>这是一个 React 自定义组件</div>;
6 }
7}
调试
组件保存后会自动进入构建状态,调试按钮将禁用,当构建成功后,即可点击调试按钮,进行组件调试操作:
调试面板左侧为组件渲染后效果,右侧为 amis schema,可以调整右侧 schema,动态调试组件效果。
例如我们修改代码如下:
我们获取 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}
可以看到上图中自定义组件已经可以正常使用了。
添加 npm 依赖
平台支持引入在线第三方 npm 库进行辅助开发
首先点击右上角依赖管理,搜索并添加最新版本的 day.js,该插件已内置安装,因此可以直接使用:
我们使用展示类,并选中 React 作为开发框架,下面我们引用 day.js 并打印一下当前时间,代码如下:
1import React from 'react';
2import dayjs from 'dayjs';
3
4export default class Test extends React.Component {
5 render() {
6 return <div>现在时间是:{dayjs().format('YYYY-DD-MM HH:mm:ss')}</div>;
7 }
8}
效果如下:
内置已安装的插件,都可以直接使用
开发框架
平台支持 React、Vue、jQuery 作为组件的开发框架
React(推荐)
平台以及 amis 本身是基于 React 开发的,因此使用 React 可以更好地整合开发组件
导出组件类
核心方式是通过 export default
来导出组件类,从而使组件生效
1import React from 'react';
2
3export default class Test extends React.Component {
4 render() {
5 return <div>这是一个 React 自定义组件</div>;
6 }
7}
使用 UI 组件库
可以使用 npm UI 组件库,例如 Ant Design。
首先依赖管理中添加 antd 组件库,然后选择展示类,和 React 作为开发框架,然后编写代码如下:
1import React from 'react';
2import {Button} from 'antd';
3
4// 引用 antd 样式
5import 'antd/dist/antd.css';
6
7export default class Test extends React.Component {
8 render() {
9 return (
10 <div>
11 <Button type="primary">Primary Button</Button>
12 <Button>Default Button</Button>
13 <Button type="dashed">Dashed Button</Button>
14 <br />
15 <Button type="text">Text Button</Button>
16 <Button type="link">Link Button</Button>
17 </div>
18 );
19 }
20}
效果如下:
上例中我们展示了 antd 的 Button 组件
Vue 2
我们下面开发一个简单的自定义组价,打印一行文本:
1export default {
2 template: '<div>这是一个 {{name}} 自定义组件</div>',
3
4 data: {
5 name: 'Vue'
6 },
7
8 methods: {
9 foo() {
10 console.log('foo');
11 }
12 },
13
14 created() {
15 this.foo();
16 }
17};
使用第三方 UI 组件库
可以使用第三方组件库,例如 Element-UI
首先依赖管理里添加 element-ui 组件库,然后新建组件,选择展示类组件,勾选 Vue 作为开发框架,代码如下:
1import {Button} from 'element-ui';
2
3// 引入 element-ui的样式
4import 'element-ui/lib/theme-chalk/index.css';
5
6export default class Test {
7 template = `
8 <div>
9 <el-button>默认按钮</el-button>
10 <el-button type="primary">主要按钮</el-button>
11 <el-button type="success">成功按钮</el-button>
12 <el-button type="info">信息按钮</el-button>
13 <el-button type="warning">警告按钮</el-button>
14 <el-button type="danger">危险按钮</el-button>
15 </div>
16 `;
17
18 components = {
19 'el-button': Button
20 };
21}
效果如下:
Vue 3
请注意,在一个应用中不能混用 Vue 2 和 Vue 3 组件,因为 vue 只有一个 package 名称,无法同时存在多个版本
Vue 3 在接口层面做了很大改动,它的使用方式和 Vue 2 不同。
默认展现的示例是:
1export 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
10 data() {
11 return {
12 message: 'Hello Vue.js!'
13 };
14 },
15
16 methods: {
17 reverseMessage() {
18 this.message = this.message.split('').reverse().join('');
19 }
20 }
21 });
22
23 return app;
24};
爱速搭会传递 createApp 方法,而插件需要返回构建好的 app 对象。
使用第三方 UI 库
首先在依赖中添加,然后通过 app.use 方法来添加这个第三方 UI 库的插件
1import './style.scss'; // 自定义样式,文件名称不可修改
2
3import ElementPlus from 'element-plus';
4import 'element-plus/dist/index.css';
5
6export default ({createApp, props, funcs}) => {
7 const app = createApp({
8 template: `
9<el-row>
10 <el-button>默认按钮</el-button>
11 <el-button type="primary">主要按钮</el-button>
12 <el-button type="success">成功按钮</el-button>
13 <el-button type="info">信息按钮</el-button>
14 <el-button type="warning">警告按钮</el-button>
15 <el-button type="danger">危险按钮</el-button>
16</el-row>
17 `
18 });
19
20 app.use(ElementPlus);
21
22 return app;
23};
jQuery
平台内部支持用 jQuery 进行开发,且进行了一层简单的封装,方便用户更好的操作。下面我们创建一个简单的自定义组件,打印一行文本:
1import $ from 'jquery';
2
3export default {
4 template: `这是个`,
5
6 /**
7 * 组件挂载的时候调用
8 */
9 onMount(props) {
10 this.foo();
11 },
12
13 /**
14 * amis props 更新的时候调用
15 */
16 onUpdate(props, prevProps) {},
17
18 /**
19 * 组件销货的时候调用
20 */
21 onUnmout(props) {},
22
23 foo() {
24 // this.$root 获取当前顶级dom
25 $(this.$root).append(' jQuery 自定义组件');
26 }
27};
效果如下:
获取 schema 配置的属性
可以在 schema 中配置属性,然后在组件中获取,例如:
React 中:
1import React from 'react';
2
3export default class Test extends React.Component {
4 render() {
5 const tip = this.props.tip;
6 return <div>提示文本: {tip}</div>;
7 }
8}
使用组件时可以配置 schema 如下:
1{
2 "type": "xxx", // 组件的 key 值
3 "tip": "这是一段提示"
4}
Vue 中:
1export default {
2 template: '<div>提示文本:{{tip}}</div>',
3
4 data: {
5 tip: '' // 需要声明一个空的值,否则可能会报错
6 }
7};
由于该 Vue 特性,需要在使用前声明一个空的默认值
效果如下:
jQuery 中
1import $ from 'jquery';
2
3export default {
4 template: `提示文本:<span id="tip"></span>`,
5
6 onMount(props) {
7 $('#tip').text(props.tip);
8 },
9
10 onUpdate(props) {
11 $('#tip').text(props.tip);
12 }
13};
效果如下:
暴露的变量和方法
render
渲染器方法,可以在自定义组件中渲染已有的 amis 组件
适用条件
- 开发框架:
React
- 组件类型:
展示类
、表单项
、选择器表单项
函数签名
1(region, node, subProps) => JSX.Element;
region
:给当前组件设置一个 keynode
:amis 组件的配置项subProps
:可以不填,额外的一些配置项
使用方法
React 中:
1import React from 'react';
2
3export default class Test extends React.Component {
4 render() {
5 const {render} = this.props;
6 return (
7 <div>
8 这是一个 React 自定义组件,
9 {render('test', {
10 type: 'button',
11 label: '这是amis按钮',
12 actionType: 'dialog',
13 dialog: {
14 title: '弹框',
15 body: '这是一个弹框'
16 }
17 })}
18 </div>
19 );
20 }
21}
效果如下:
onAction
调用 amis 内置的行为,可参考 行为
适用条件
- 开发框架:React、Vue、jQuery
- 组件类型:展示类、表单项、选择器表单项
函数签名
1(event, action, ctx) => void;
event
:可忽略,传入null
即可;action
:传入需要执行的行为对象配置,参考 行为;ctx
:给当前行为内传入一些数据。如果没有则传入空对象{}
,否则会报错
使用方法
我们来通过该方法实现:点击按钮,然后调起一个 amis 弹框。
React 中
1import React from 'react';
2
3export default class Test extends React.Component {
4 constructor() {
5 super();
6 this.handleClick = this.handleClick.bind(this);
7 }
8
9 handleClick() {
10 const onAction = this.props.onAction;
11
12 onAction(
13 null,
14 {
15 actionType: 'dialog',
16 dialog: {
17 title: '弹框',
18 body: '这是一个amis弹框'
19 }
20 },
21 {}
22 );
23 }
24
25 render() {
26 return <button onClick={this.handleClick}>调起 amis 弹框</button>;
27 }
28}
Vue 中
1export default {
2 template: '<button v-on:click="handleClick">调起 amis 弹框</button>',
3
4 data: {},
5
6 methods: {
7 handleClick() {
8 this.$emit('onAction', [
9 null,
10 {
11 actionType: 'dialog',
12 dialog: {
13 title: '弹框',
14 body: '这是一个amis弹框'
15 }
16 },
17 {}
18 ]);
19 }
20 }
21};
jQuery 中
1import $ from 'jquery';
2
3export default {
4 template: `<button id="btn">调起 amis 弹框</button>`,
5
6 onMount(props) {
7 $('#btn').click(() => {
8 props.onAction(
9 null,
10 {
11 actionType: 'dialog',
12 dialog: {
13 title: '弹框',
14 body: '这是一个amis弹框'
15 }
16 },
17 {}
18 );
19 });
20 }
21};
value 和 onChange
当编写表单项类型的自定义组件时,最重要的是与 Form 数据域的通信,而实现该通信的核心就是 value 属性和 onChange 方法
适用条件
- 开发框架:React、Vue、jQuery
- 组件类型:普通表单项、选择器表单项
使用方法
核心思路是:
- 拿到 props 中的 value 属性,并赋值给自定义组件内输入框
- 监听自定义组件内输入框 change 事件或 value 值变化,如果有变化,通过 onChange 事件,将新的 value 值同步给 amis 层
下面来演示不同框架下的使用示例:
React 中
1import React from 'react';
2
3export default class Test extends React.PureComponent {
4 constructor() {
5 this.handleChange = this.handleChange.bind(this);
6 }
7
8 // 监听 input 的 change 事件,并同步value值
9 handleChange(event) {
10 const onChange = this.props.onChange;
11 // 调用amis onChange方法,同步最新的value值
12 onChange(event.target.value);
13 }
14
15 render() {
16 // 获取 props 中的 value 属性,并赋值给input
17 const {value} = this.props;
18 return <input type="text" value={value} onChange={this.handleChange} />;
19 }
20}
效果如下:
更改 input 值,可以观察表单数据域的变化。
Vue 中
1export default {
2 // 获取 value 属性并绑定给 input 输入框
3 template: `<input type="text" v-model="value" />`,
4
5 watch: {
6 value: function (newValue, oldValue) {
7 // 通过 $emit 调用 amis 的 onChange 事件,同步 value 值
8 this.$emit('onChange', newValue);
9 }
10 }
11};
vue 中可以利用 watch 监听器来实现 value 同步逻辑。
效果如下;
更改 input 值,可以观察表单数据域的变化。
jQuery 中
1import $ from 'jquery';
2
3export default {
4 template: `<input type="text" id="input" />`,
5
6 onMount(props) {
7 // 获取 props 中 value 属性
8 $('#input').attr('value', props.value);
9
10 // 给输入框绑定 input 监听事件,同步 value 值
11 $('#input').on('input', function (e) {
12 // 调用amis的 onChange 方法
13 props.onChange(e.target.value);
14 });
15 }
16};
效果如下:
更改 input 值,可以观察表单数据域的变化。
onBulkChange
该方法类似于 onChange,不同点在于,它可以批量修改表单项的值。
1import React from 'react';
2
3export default class Test extends React.PureComponent {
4 constructor() {
5 this.handleChange = this.handleChange.bind(this);
6 }
7
8 handleChange(event) {
9 const {name, other, onBulkChange} = this.props;
10 const value = event.target.value;
11 // 调用amis onChange方法,变更表单项值
12 onBulkChange({
13 [name]: value,
14 [other]: value
15 });
16 }
17
18 render() {
19 // 获取表单项 value 属性
20 const {value} = this.props;
21
22 return <input type="text" value={value} onChange={this.handleChange} />;
23 }
24}
例如上例中,我们实现一个:修改当前输入框,会同步修改另外一个数据框的值,具体 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
,批量修改数据域值
效果如下:
vue 和 jquery 使用逻辑相同,具体使用语法方法见 onChange
options
选择器表单项特有的属性,一个选择器组件总有一组选项,可以提供给用户勾选一项或多项,默认该属性为空数组
适用范围
- 开发框架:React、Vue、jQuery
- 组件类型:选择器表单项
手动配置
你可以手动在 schema 中配置 options 吗,然后在组件内获取并使用,例如:
1import React from 'react';
2
3export default class Test extends React.PureComponent {
4 constructor() {
5 this.handleChange = this.handleChange.bind(this);
6 }
7
8 handleChange(event) {
9 // 调用amis onToggle 方法,变更选择器表单项值
10 const {onToggle, options} = this.props;
11 const option = options.find(o => o.value === event.target.value);
12 onToggle(option);
13 }
14
15 render() {
16 // 获取表单项 value 和 options 属性
17 const {value, options} = this.props;
18
19 return (
20 <select value={value} onChange={this.handleChange}>
21 {options.map(option => (
22 <option key={option.value} value={option.value}>
23 {option.label}
24 </option>
25 ))}
26 </select>
27 );
28 }
29}
编辑 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
:一个对象数组,里面有若干选项
使用方法
1import React from 'react';
2
3export default class Test extends React.PureComponent {
4 constructor() {
5 this.handleChange = this.handleChange.bind(this);
6 }
7
8 // 组件挂载时,调用 setOptions,设置 options
9 componentDidMount() {
10 const {setOptions} = this.props;
11 setOptions([
12 {
13 label: 'Option A',
14 value: 'a'
15 },
16 {
17 label: 'Option B',
18 value: 'b'
19 },
20 {
21 label: 'Option C',
22 value: 'c'
23 }
24 ]);
25 }
26
27 handleChange(event) {
28 // 调用amis onToggle 方法,变更选择器表单项值
29 const {onToggle, options} = this.props;
30 const option = options.find(o => o.value === event.target.value);
31 onToggle(option);
32 }
33
34 render() {
35 // 获取表单项 value 和 options 属性
36 const {value, options} = this.props;
37
38 return (
39 <select value={value} onChange={this.handleChange}>
40 {options.map(option => (
41 <option key={option.value} value={option.value}>
42 {option.label}
43 </option>
44 ))}
45 </select>
46 );
47 }
48}
selectedOptions
由于 拼接符-delimiter、拼接值-joinvalues、提取多选值-extractvalue 的存在,value 值格式可以是多种格式。
selectedOptions
则永远为当前选中的值的数组形式,方便逻辑操作。
适用范围
- 开发框架:React、Vue、jQuery
- 组件类型:选择器表单项
onToggle
选择器表单项专用属性,类似于 onChange,不同的是,该方法需要传入一个完整的选项对象,可以设置选项切换勾选。
适用范围
- 开发框架:React、Vue、jQuery
- 组件类型:选择器表单项
函数签名
1(option) => void;
option
:一个选项对象
使用方法
1import React from 'react';
2
3export default class Test extends React.PureComponent {
4 constructor() {
5 this.handleChange = this.handleChange.bind(this);
6 }
7
8 handleChange(event) {
9 // 调用amis onToggle 方法,变更选择器表单项值
10 const {onToggle, options} = this.props;
11 const option = options.find(o => o.value === event.target.value);
12 onToggle(option);
13 }
14
15 render() {
16 // 获取表单项 value 和 options 属性
17 const {value, options} = this.props;
18
19 return (
20 <select value={value} onChange={this.handleChange}>
21 {options.map(option => (
22 <option key={option.value} value={option.value}>
23 {option.label}
24 </option>
25 ))}
26 </select>
27 );
28 }
29}
与 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
使用方法
1import React from 'react';
2
3export default class Test extends React.PureComponent {
4 constructor() {
5 this.handleChange = this.handleChange.bind(this);
6 this.handleSelectAll = this.handleSelectAll.bind(this);
7 }
8
9 handleChange(event) {
10 // 调用amis onToggle 方法,变更选择器表单项值
11 const {onToggle, options} = this.props;
12 const option = options.find(o => o.value === event.target.value);
13 onToggle(option);
14 }
15
16 handleSelectAll() {
17 const {onToggleAll} = this.props;
18 onToggleAll();
19 }
20
21 render() {
22 // 获取表单项 value 和 options 属性
23 const {value, options} = this.props;
24
25 return (
26 <>
27 <select value={value} onChange={this.handleChange}>
28 {options.map(option => (
29 <option key={option.value} value={option.value}>
30 {option.label}
31 </option>
32 ))}
33 </select>
34 <button onClick={this.handleSelectAll}>全选</button>
35 </>
36 );
37 }
38}
效果如下: